def _connect_supply(self, sup_warr, sup_intv, hm_w_sup, round_up=False): # gather list of track indices and wires warr_list = sup_warr.to_warr_list() min_tid = max_tid = None for warr in warr_list: tid = warr.track_id.base_index if min_tid is None: min_tid = max_tid = tid else: min_tid = min(tid, min_tid) max_tid = max(tid, max_tid) hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 sup_idx = self.grid.get_middle_track(sup_intv[0], sup_intv[1] - 1, round_up=round_up) xl = self.grid.track_to_coord(self.conn_layer, min_tid, unit_mode=True) xr = self.grid.track_to_coord(self.conn_layer, max_tid, unit_mode=True) tl = self.grid.coord_to_nearest_track(vm_layer, xl, half_track=True, mode=1, unit_mode=True) tr = self.grid.coord_to_nearest_track(vm_layer, xr, half_track=True, mode=-1, unit_mode=True) num = int((tr - tl + 2) // 2) tid = TrackID(vm_layer, tl, num=num, pitch=2) sup = self.connect_to_tracks(warr_list, TrackID(hm_layer, sup_idx, width=hm_w_sup)) return self.connect_to_tracks(sup, tid, min_len_mode=0)
def _connect_ser_div_mux(self, ym_layer, tr_manager, serb, sert, div, mux, x0, clk_locs, vddo_list, vsso_list, vddi_list, vssi_list, test_box, show_pins): # get track locations ym_w_clk = tr_manager.get_width(ym_layer, 'clk') ym_w_sh = tr_manager.get_width(ym_layer, 'sh') ym_w_sig = tr_manager.get_width(ym_layer, 'sig') tr0 = self.grid.find_next_track(ym_layer, x0, half_track=True, unit_mode=True) en2_tid = TrackID(ym_layer, tr0 + clk_locs[0], width=ym_w_clk) sh_tid = TrackID(ym_layer, tr0 + clk_locs[1], width=ym_w_sh, num=2, pitch=clk_locs[4] - clk_locs[1]) out0_tid = TrackID(ym_layer, tr0 + clk_locs[5], width=ym_w_sig) out1_tid = TrackID(ym_layer, tr0 + clk_locs[6], width=ym_w_sig) # connect clocks pidx = tr0 + clk_locs[2] nidx = tr0 + clk_locs[3] clkp = list( chain(div.port_pins_iter('clkp'), mux.port_pins_iter('clkp'))) clkn = list( chain(div.port_pins_iter('clkn'), mux.port_pins_iter('clkn'))) clkp, clkn = self.connect_differential_tracks(clkp, clkn, ym_layer, pidx, nidx, width=ym_w_clk) self.add_pin('clkp', clkp, show=show_pins) self.add_pin('clkn', clkn, show=show_pins) # draw shield connections mux_yb, mux_yt = test_box.get_interval('y', unit_mode=True) for name, o_list, i_list in (('VDD', vddo_list, vddi_list), ('VSS', vsso_list, vssi_list)): for warr in div.port_pins_iter(name): yb, yt = warr.track_id.get_bounds(self.grid, unit_mode=True) if yt < mux_yb or yb > mux_yt: o_list.append(warr) else: i_list.append(warr) # connect seriailzier to mux self.connect_to_tracks( [serb.get_pin('out'), mux.get_pin('data0')], out0_tid) self.connect_to_tracks( [sert.get_pin('out'), mux.get_pin('data1')], out1_tid) scan_list = [div.get_pin('scan_div<3>'), div.get_pin('scan_div<2>')] self.connect_to_tracks(scan_list, sh_tid) self.connect_to_tracks(vsso_list, sh_tid) # draw en2 connections self.connect_to_tracks(div.get_all_port_pins('en2'), en2_tid)
def connect_resistors(self, ndum, nser, bias_idx): nx = 2 * (nser + ndum) biasp = [] biasn = [] outp = outn = None for idx in range(ndum): biasp.extend(self.get_res_ports(0, idx)) biasn.extend(self.get_res_ports(0, nx - 1 - idx)) for idx in range(ndum, nser + ndum): cpl = self.get_res_ports(0, idx) cpr = self.get_res_ports(0, nx - 1 - idx) conn_par = (idx - ndum) % 2 if idx == ndum: biasp.append(cpl[1 - conn_par]) biasn.append(cpr[1 - conn_par]) if idx == nser + ndum - 1: outp = cpl[conn_par] outn = cpr[conn_par] else: npl = self.get_res_ports(0, idx + 1) npr = self.get_res_ports(0, nx - 2 - idx) self.connect_wires([npl[conn_par], cpl[conn_par]]) self.connect_wires([npr[conn_par], cpr[conn_par]]) biasp = self.connect_wires(biasp) biasn = self.connect_wires(biasn) # connect bias wires to vertical tracks vm_layer = self.bot_layer_id + 1 t0 = self.grid.find_next_track(vm_layer, 0, half_track=True, mode=1, unit_mode=True) t1 = self.grid.find_next_track(vm_layer, self.bound_box.right_unit, half_track=True, mode=-1, unit_mode=True) bp_tid = TrackID(vm_layer, t0 + bias_idx + 1) bn_tid = TrackID(vm_layer, t1 - bias_idx - 1) biasp = self.connect_to_tracks(biasp, bp_tid) biasn = self.connect_to_tracks(biasn, bn_tid) vdd = self.add_wires(vm_layer, t0, biasp.lower_unit, biasp.upper_unit, num=2, pitch=t1 - t0, unit_mode=True) xl = self.grid.get_wire_bounds(vm_layer, t0 + 2, unit_mode=True)[1] xr = self.grid.get_wire_bounds(vm_layer, t1 - 2, unit_mode=True)[0] return vdd, biasp, biasn, outp, outn, xl, xr
def draw_layout(self): row_layout_info = self.params['row_layout_info'] num_col = self.params['num_col'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] sup_tids = self.params['sup_tids'] end_mode = self.params['end_mode'] abut_mode = self.params['abut_mode'] abut_sp = self.params['abut_sp'] show_pins = self.params['show_pins'] hm_layer = self.conn_layer + 1 xm_layer = hm_layer + 2 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) hm_w_sup = tr_manager.get_width(hm_layer, 'sup') xm_w_sup = tr_manager.get_width(xm_layer, 'sup') inc_col = 0 if abut_mode & 1 != 0: # abut on left inc_col += abut_sp col_start = abut_sp else: col_start = 0 if abut_mode & 2 != 0: # abut on right inc_col += abut_sp self.set_rows_direct(row_layout_info, num_col=num_col, end_mode=end_mode) # draw substrate vss_w, vdd_w = self._draw_substrate(col_start, num_col, num_col - inc_col) # fill space self.fill_space() # connect supply wires vss_intv = self.get_track_interval(0, 'ds') vdd_intv = self.get_track_interval(self.num_rows - 1, 'ds') vss = self._connect_supply(vss_w, vss_intv, hm_w_sup, round_up=False) vdd = self._connect_supply(vdd_w, vdd_intv, hm_w_sup, round_up=True) if sup_tids is not None: vdd = self.connect_to_tracks(vdd, TrackID(xm_layer, sup_tids[1], width=xm_w_sup)) vss = self.connect_to_tracks(vss, TrackID(xm_layer, sup_tids[0], width=xm_w_sup)) self.add_pin('VDD', vdd, show=show_pins) self.add_pin('VSS', vss, 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
def _connect_lv_por_vm( template: MOSBase, rstl: WireArray, rstr: WireArray, rstc: WireArray, rst_idx_list: List[HalfInt], rstc_idx: int) -> Tuple[WireArray, WireArray, WireArray]: vm_layer = template.conn_layer + 2 vm_w = template.tr_manager.get_width(vm_layer, 'sig') rstl = template.connect_to_tracks( rstl, TrackID(vm_layer, rst_idx_list[0], width=vm_w)) rstc = template.connect_to_tracks( rstc, TrackID(vm_layer, rst_idx_list[rstc_idx], width=vm_w)) rstr = template.connect_to_tracks( rstr, TrackID(vm_layer, rst_idx_list[3], width=vm_w)) return rstl, rstr, rstc
def _connect_mirror(self, offset, loc1, loc2, port1, port2): r1, c1 = loc1 r2, c2 = loc2 for sgn in (-1, 1): cur_r1 = offset + sgn * r1 cur_r2 = offset + sgn * r2 if sgn < 0: cur_r1 -= 1 cur_r2 -= 1 if sgn < 0: cur_port1 = 1 - port1 cur_port2 = 1 - port2 else: cur_port1 = port1 cur_port2 = port2 wa1 = self.get_res_ports(cur_r1, c1)[cur_port1] wa2 = self.get_res_ports(cur_r2, c2)[cur_port2] if wa1.track_id.base_index == wa2.track_id.base_index: self.connect_wires([wa1, wa2]) else: mode = -1 if c1 % 2 == 0 else 1 vm_layer = wa1.layer_id + 1 vm = self.grid.coord_to_nearest_track(vm_layer, wa1.middle_unit, mode=mode, half_track=True, unit_mode=True) self.connect_to_tracks([wa1, wa2], TrackID(vm_layer, vm))
def to_warr(self, grid: RoutingGrid) -> WireArray: return WireArray( TrackID(self.layer, self.track, width=self.width, num=self.num, pitch=self.pitch, grid=grid), self.lower, self.upper)
def _connect_dummies(self, ncol, nr1, nr2, ndumr, ndumc): res_num_iter = chain(repeat(0, ndumc), repeat(nr1, ncol), repeat(nr2, ncol), repeat(0, ndumc)) nrow_half = max(nr1, nr2) + ndumr bot_warrs, top_warrs = [], [] for col_idx, res_num in enumerate(res_num_iter): mode = -1 if col_idx % 2 == 0 else 1 if res_num == 0: cur_ndum = nrow_half * 2 bot_idx_list = [0] else: cur_ndum = nrow_half - res_num bot_idx_list = [0, nrow_half + res_num] for bot_idx in bot_idx_list: top_idx = bot_idx + cur_ndum warr_list = [] for ridx in range(bot_idx, top_idx): bp, tp = self.get_res_ports(ridx, col_idx) warr_list.append(bp) warr_list.append(tp) vm_layer = warr_list[0].layer_id + 1 vm = self.grid.coord_to_nearest_track(vm_layer, warr_list[0].middle_unit, mode=mode, half_track=True, unit_mode=True) sup_warr = self.connect_to_tracks(warr_list, TrackID(vm_layer, vm)) if bot_idx == 0: bot_warrs.append(sup_warr) if bot_idx != 0 or res_num == 0: top_warrs.append(sup_warr) hm_layer = bot_warrs[0].layer_id + 1 hm_pitch = self.grid.get_track_pitch(hm_layer, unit_mode=True) num_hm_tracks = self.array_box.height_unit // hm_pitch btr = self.connect_to_tracks(bot_warrs, TrackID(hm_layer, 0), track_lower=0) ttr = self.connect_to_tracks(top_warrs, TrackID(hm_layer, num_hm_tracks - 1), track_lower=0) return ttr, btr
def _connect_supplies(self, supl_list, supr_list, show_pins): vm_layer = self.bot_layer_id + 1 xm_layer = vm_layer + 1 for sup_list, mode, name in ((supl_list, -1, 'VSSL'), (supr_list, 1, 'VSSR')): xc = sup_list[0].middle_unit vm_tr = self.grid.coord_to_nearest_track(vm_layer, xc, half_track=True, mode=mode, unit_mode=True) sup = self.connect_to_tracks(sup_list, TrackID(vm_layer, vm_tr)) xm_tr = self.grid.coord_to_nearest_track(xm_layer, sup.middle_unit, half_track=True, unit_mode=True) sup = self.connect_to_tracks(sup, TrackID(xm_layer, xm_tr), min_len_mode=mode) self.add_pin(name, sup, label='VSS:', show=show_pins)
def _connect_mux_buf(self, ym_layer, tr_manager, mux, bufb, buft, vddo_list, vsso_list, vddi_list, vssi_list, test_box, show_pins): # gather supplies xl = 0 test_yb, test_yt = test_box.get_interval('y', unit_mode=True) for name, o_list, i_list in (('VDD', vddo_list, vddi_list), ('VSS', vsso_list, vssi_list)): for warr in mux.port_pins_iter(name): xl = max(xl, warr.upper_unit) yb, yt = warr.track_id.get_bounds(self.grid, unit_mode=True) if yt < test_yb or yb > test_yt: o_list.append(warr) else: i_list.append(warr) vddi_list.extend(bufb.port_pins_iter('VDD')) vddi_list.extend(buft.port_pins_iter('VDD')) vssi_list.extend(bufb.port_pins_iter('VSS')) vssi_list.extend(buft.port_pins_iter('VSS')) # connect wires ym_w_sig = tr_manager.get_width(ym_layer, 'sig') xr = min(vddi_list[-1].lower_unit, vssi_list[-1].lower_unit) ym_tidx = self.grid.coord_to_nearest_track(ym_layer, (xl + xr) // 2, mode=0, unit_mode=True) warrs = [mux.get_pin('outp'), bufb.get_pin('in')] self.connect_to_tracks(warrs, TrackID(ym_layer, ym_tidx, width=ym_w_sig)) warrs = [mux.get_pin('outn'), buft.get_pin('in')] self.connect_to_tracks(warrs, TrackID(ym_layer, ym_tidx, width=ym_w_sig)) # export mux self.add_pin('outp', bufb.get_pin('out'), show=show_pins) self.add_pin('outn', buft.get_pin('out'), show=show_pins)
def _export_to_ym(self, port, bot_layer): warr = port for off in range(1, 4): next_layer = bot_layer + off next_width = self.w_tracks[off] next_tr = self.grid.coord_to_nearest_track(next_layer, warr.middle_unit, half_track=True, mode=0, unit_mode=True) tid = TrackID(next_layer, next_tr, width=next_width) warr = self.connect_to_tracks(warr, tid, min_len_mode=0) return warr
def _connect_ser_amp(self, ym_layer, tr_manager, ser, amp, x_route, ibias_locs, ym_tr_w_ibias, show_pins): wp = self.connect_wires([ser.get_pin('outp'), amp.get_pin('inp')])[0] wn = self.connect_wires([ser.get_pin('outn'), amp.get_pin('inn')])[0] lower = min( wp.track_id.get_bounds(self.grid, unit_mode=True)[0], wn.track_id.get_bounds(self.grid, unit_mode=True)[0]) upper = amp.bound_box.top_unit ym_tr_w_sh = tr_manager.get_width(ym_layer, 'sh') tr0 = self.grid.find_next_track(ym_layer, x_route, mode=1, half_track=True, unit_mode=True) sh = self.add_wires(ym_layer, tr0 + ibias_locs[0], lower, upper, width=ym_tr_w_sh, num=2, pitch=ibias_locs[2] - ibias_locs[0], unit_mode=True) ibias_tid = TrackID(ym_layer, tr0 + ibias_locs[1], width=ym_tr_w_ibias) self.add_pin('ibias', self.connect_to_tracks(amp.get_pin('ibias'), ibias_tid, track_lower=lower, track_upper=upper, unit_mode=True), show=show_pins) for name in ('ser_reset', 'div_en', 'clock_tx_div', 'clkp', 'clkn'): self.reexport(ser.get_port(name), show=show_pins) for idx in range(32): self.reexport(ser.get_port('data_tx<%d>' % idx), show=show_pins) self.reexport(ser.get_port('VDD'), label='VDD:', show=show_pins) self.reexport(ser.get_port('VSS'), label='VSS:', show=show_pins) return sh
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) flip_tile: bool = self.params['flip_tile'] self.draw_base(pinfo, flip_tile=flip_tile) seg_dict: Dict[str, int] = self.params['seg_dict'] w_dict: Dict[str, int] = self.params['w_dict'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] vertical_rst: bool = self.params['vertical_rst'] substrate_row: bool = self.params['substrate_row'] min_sep = self.min_sep_col seg_buf = seg_dict.get('buf', 1) seg_in = seg_dict.get('in', 1) seg_mux = seg_dict.get('mux', 1) seg_keep = seg_dict.get('keep', 1) seg_pass = seg_dict.get('pass', 1) seg_rst = seg_dict.get('rst', 1) seg_out = seg_dict.get('out', 1) tile0 = self.params['tile0'] tile1 = self.params['tile1'] if substrate_row: vss0_tid = None vdd_tid = None vss1_tid = None else: vss0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile0) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sup') vss1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile1) place_info = self.get_tile_pinfo(tile0) default_wn = place_info.get_row_place_info(ridx_n).row_info.width default_wp = place_info.get_row_place_info(ridx_p).row_info.width wp_buf = w_dict.get('p_buf', default_wp) wn_buf = w_dict.get('n_buf', default_wn) wp_in = w_dict.get('p_in', default_wp) wn_in = w_dict.get('n_in', default_wn) wp_mux = w_dict.get('p_mux', default_wp) wn_mux = w_dict.get('n_mux', default_wn) wp_keep = w_dict.get('p_keep', default_wp) wn_keep = w_dict.get('n_keep', default_wn) wp_pass = w_dict.get('p_pass', default_wp) wn_pass = w_dict.get('n_pass', default_wn) wp_rst = w_dict.get('p_rst', default_wp) wn_rst = w_dict.get('n_rst', default_wn) wp_out = w_dict.get('p_out', default_wp) wn_out = w_dict.get('n_out', default_wn) # tracks t0_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t0_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t1_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) t1_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) # placement # tile 0 cur_col = 0 mos_in_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_in_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_in + min_sep mos_keep_n = self.add_mos(ridx_n, cur_col, seg_keep, w=wn_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_keep_p = self.add_mos(ridx_p, cur_col, seg_keep, w=wp_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_keep + min_sep mos_pass_n = self.add_mos(ridx_n, cur_col, seg_pass, w=wn_pass, g_on_s=False, tile_idx=tile0) mos_pass_p = self.add_mos(ridx_p, cur_col, seg_pass, w=wp_pass, g_on_s=False, tile_idx=tile0) cur_col += seg_pass + min_sep mos_buf_n = self.add_mos(ridx_n, cur_col, 2 * seg_buf, w=wn_buf, g_on_s=True, sep_g=True, tile_idx=tile0) mos_buf_p = self.add_mos(ridx_p, cur_col, 2 * seg_buf, w=wp_buf, g_on_s=True, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_buf + min_sep mos_out_n = self.add_mos(ridx_n, cur_col, 2 * seg_out, w=wn_out, tile_idx=tile0) mos_out_p = self.add_mos(ridx_p, cur_col, 2 * seg_out, w=wp_out, tile_idx=tile0) col_tot = cur_col + 2 * seg_out # tile 1 cur_col = 0 mos_si_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_si_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col += 2 * seg_in + min_sep mos_mux_n = self.add_mos(ridx_n, cur_col, seg_mux, w=wn_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_mux_p = self.add_mos(ridx_p, cur_col, seg_mux, w=wp_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst - min_sep - 4 * seg_rst sub_col = cur_col mos_rst_n = self.add_mos(ridx_n, cur_col, 2 * seg_rst, w=wn_rst, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_rst_p = self.add_mos(ridx_p, cur_col, 4 * seg_rst, w=wp_rst, g_on_s=True, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst mos_rst2_n = self.add_mos(ridx_n, cur_col, seg_rst, w=wn_rst, g_on_s=True, tile_idx=tile1) mos_rst2_p = self.add_mos(ridx_p, cur_col, seg_rst, w=wp_rst, g_on_s=False, tile_idx=tile1) if substrate_row: self._substrate_row_intvl.append((sub_col, self.num_cols-sub_col)) for _tidx in range(pinfo[0].num_tiles): _pinfo = self.get_tile_pinfo(_tidx) for _ridx in range(_pinfo.num_rows): rtype = _pinfo.get_row_place_info(_ridx).row_info.row_type if rtype.is_substrate: warrs = self.add_substrate_contact(_ridx, sub_col, tile_idx=_tidx, seg=self.num_cols-sub_col) sup_name = 'VDD' if rtype is MOSType.ntap else 'VSS' self.add_pin(f'{sup_name}_sub', warrs, label=f'{sup_name}:') self.set_mos_size() # vertical routing track planning grid = self.grid tr_manager = self.tr_manager conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 vm_w = tr_manager.get_width(vm_layer, 'sig') vm_se_tidx = grid.coord_to_track(vm_layer, 0) vm_seb_tidx = tr_manager.get_next_track(vm_layer, vm_se_tidx, 'sig', 'sig') vm_o1_tidx = tr_manager.get_next_track(vm_layer, vm_seb_tidx, 'sig', 'sig') vm_ck1_tidx = tr_manager.get_next_track(vm_layer, vm_o1_tidx, 'sig', 'sig') x_ck1 = grid.track_to_coord(conn_layer, mos_mux_p.g0.track_id.base_index) vm_ck1_tidx = max(vm_ck1_tidx, grid.coord_to_track(vm_layer, x_ck1, mode=RoundMode.LESS_EQ)) x_ck2 = grid.track_to_coord(conn_layer, mos_rst_p.g[-1].track_id.base_index) vm_ck2_tidx = grid.coord_to_track(vm_layer, x_ck2, mode=RoundMode.GREATER_EQ) vm_o5_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig') vm_o42_tidx = tr_manager.get_next_track(vm_layer, vm_o5_tidx, 'sig', 'sig') x_ckb = grid.track_to_coord(conn_layer, mos_buf_n.s[0].track_id.base_index) vm_ckb_tidx = grid.coord_to_track(vm_layer, x_ckb, mode=RoundMode.GREATER_EQ) vm_seb2_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig', up=False) x_o41 = grid.track_to_coord(conn_layer, mos_pass_n.d.track_id.base_index) vm_o41_tidx = grid.coord_to_track(vm_layer, x_o41, mode=RoundMode.LESS_EQ) x_o2 = x_o41 - self.place_info.sd_pitch * 2 vm_o2_tidx = grid.coord_to_track(vm_layer, x_o2, mode=RoundMode.LESS_EQ) # routing # supplies vdd_list = [mos_in_p.s, mos_keep_p.d, mos_buf_p.d, mos_out_p.d, mos_si_p.s, mos_mux_p.d, mos_rst_p.s[0:2], mos_rst2_p.s] vss0_list = [mos_in_n.s, mos_keep_n.d, mos_buf_n.d, mos_out_n.d] vss1_list = [mos_si_n.s, mos_mux_n.d, mos_rst_n.d] if substrate_row: if tile0 < tile1: self.add_pin('VSS1', vss1_list, label='VSS:') self.add_pin('VSS0', vss0_list, label='VSS:') else: self.add_pin('VSS1', vss0_list, label='VSS:') self.add_pin('VSS0', vss1_list, label='VSS:') self.add_pin('VDD', vdd_list, label='VDD:') else: self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss0_list, vss0_tid), connect=True) self.add_pin('VSS', self.connect_to_tracks(vss1_list, vss1_tid), connect=True) # input self.add_pin('in', self.connect_to_tracks([mos_in_n.g[0], mos_in_p.g[0]], t0_pg1_tid)) # scan in self.add_pin('scan_in', self.connect_to_tracks([mos_si_n.g[0], mos_si_p.g[0]], t1_ng1_tid)) # scan enable se = self.connect_to_tracks([mos_in_p.g[1], mos_buf_p.g[1], mos_buf_n.g[1]], t0_pg0_tid) se_top = self.connect_to_tracks(mos_si_n.g[1], t1_ng0_tid, min_len_mode=MinLenMode.LOWER) se_vm = self.connect_to_tracks([se, se_top], TrackID(vm_layer, vm_se_tidx, width=vm_w)) self.add_pin('scan_en', se) self.add_pin('scan_en_vm', se_vm, hide=True) # scan enable bar seb0 = self.connect_to_tracks(mos_buf_n.s[1], t0_nd0_tid, min_len_mode=MinLenMode.UPPER) seb1 = self.connect_to_tracks(mos_in_n.g[1], t0_ng1_tid, min_len_mode=MinLenMode.LOWER) seb2 = self.connect_to_tracks(mos_buf_p.s[1], t0_pd0_tid, min_len_mode=MinLenMode.LOWER) seb3 = self.connect_to_tracks(mos_si_p.g[1], t1_pg0_tid, min_len_mode=MinLenMode.LOWER) self.connect_to_tracks([seb1, seb2, seb3], TrackID(vm_layer, vm_seb_tidx, width=vm_w)) self.connect_to_tracks([seb0, seb2], TrackID(vm_layer, vm_seb2_tidx, width=vm_w)) # clk ck = self.connect_to_tracks([mos_keep_n.g[0], mos_pass_n.g, mos_buf_n.g[0], mos_buf_p.g[0]], t0_ng0_tid) ck1 = self.connect_to_tracks(mos_mux_p.g0, t1_pg0_tid, min_len_mode=MinLenMode.UPPER) ck_vm = self.connect_to_tracks([ck, ck1], TrackID(vm_layer, vm_ck1_tidx, width=vm_w)) ck2 = self.connect_to_tracks(mos_rst_p.g[-1], t1_pg0_tid, min_len_mode=MinLenMode.UPPER) self.connect_to_tracks([ck, ck2], TrackID(vm_layer, vm_ck2_tidx, width=vm_w)) self.add_pin('clk', ck) self.add_pin('clk_vm', ck_vm, hide=True) # clkb ckb0 = self.connect_to_tracks(mos_buf_n.s[0], t0_nd1_tid, min_len_mode=MinLenMode.UPPER) ckb1 = self.connect_to_tracks([mos_keep_p.g[0], mos_pass_p.g], t0_pg1_tid) ckb2 = self.connect_to_tracks(mos_buf_p.s[0], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) ckb3 = self.connect_to_tracks([mos_mux_n.g[0], mos_rst2_n.g], t1_ng0_tid) self.connect_to_tracks([ckb0, ckb1, ckb2, ckb3], TrackID(vm_layer, vm_ckb_tidx, width=vm_w)) # o1 o10 = self.connect_to_tracks(mos_in_n.d, t0_nd1_tid, min_len_mode=MinLenMode.LOWER) o11 = self.connect_to_tracks([mos_in_p.d, mos_si_p.d], t1_pd0_tid, min_len_mode=MinLenMode.MIDDLE) o12 = self.connect_to_tracks([mos_mux_n.g[1], mos_mux_p.g[1]], t1_ng1_tid) o13 = self.connect_to_tracks(mos_si_n.d, t1_nd1_tid, min_len_mode=MinLenMode.MIDDLE) self.connect_to_tracks([o10, o11, o12, o13], TrackID(vm_layer, vm_o1_tidx, width=vm_w)) # o2 o20 = self.connect_to_tracks(mos_keep_n.s, t0_nd1_tid) o21 = self.connect_to_tracks([mos_keep_p.s, mos_mux_p.s], t1_pd1_tid) o22 = self.connect_to_tracks([mos_rst_n.g[0], mos_rst_p.g[0]], t1_pg0_tid) o23 = self.connect_to_tracks(mos_mux_n.s, t1_nd0_tid) self.connect_to_tracks([o20, o21, o22, o23], TrackID(vm_layer, vm_o2_tidx, width=vm_w)) # o3 o30 = self.connect_to_tracks([mos_keep_n.g[1], mos_keep_p.g[1]], t0_ng1_tid) o31 = self.connect_to_tracks(mos_rst_p.d[0], t1_pd0_tid) o32 = self.connect_to_tracks(mos_rst_n.s[0], t1_nd1_tid) o3_track = self.connect_wires([mos_pass_n.s, mos_pass_p.s]) self.connect_to_track_wires([o30, o31, o32], o3_track) # o4 o40 = self.connect_to_tracks(mos_pass_n.d, t0_nd0_tid, min_len_mode=MinLenMode.LOWER) o41 = self.connect_to_tracks([mos_out_n.g, mos_out_p.g], t0_pg1_tid) o42 = self.connect_to_tracks(mos_pass_p.d, t0_pd1_tid, min_len_mode=MinLenMode.LOWER) o43 = self.connect_to_tracks(mos_rst_p.d[1], t1_pd1_tid) o44 = self.connect_to_tracks(mos_rst2_n.s, t1_nd0_tid) self.connect_to_tracks([o40, o42, o43, o44], TrackID(vm_layer, vm_o41_tidx, width=vm_w)) self.connect_to_tracks([o41, o43], TrackID(vm_layer, vm_o42_tidx, width=vm_w)) # tp and tn self.connect_to_tracks([mos_rst_n.s[-1], mos_rst2_n.d], t1_nd1_tid) self.connect_to_tracks([mos_rst_p.s[-1], mos_rst2_p.d], t1_pd0_tid) # o5 o50 = self.connect_to_tracks([mos_out_n.s[0], mos_out_p.s[0]], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) o51 = self.connect_to_tracks([mos_rst_n.g[-1], mos_rst2_p.g], t1_ng1_tid) self.connect_to_tracks([o50, o51], TrackID(vm_layer, vm_o5_tidx, width=vm_w)) # output out_idx = sig_locs.get('out', t0_ng1_tid.base_index) out = self.connect_to_tracks([mos_out_n.s[1], mos_out_p.s[1]], TrackID(hm_layer, out_idx)) self.add_pin('out', out) # rstlb rstlb = self.connect_to_tracks([mos_rst_n.g[1], mos_rst_p.g[1]], t1_pg1_tid) self.add_pin('rstlb', rstlb) if vertical_rst: vm_idx = self.grid.coord_to_track(vm_layer, rstlb.middle, mode=RoundMode.NEAREST) avail = self.get_available_tracks(vm_layer, vm_ck1_tidx, vm_ck2_tidx, 0, self.bound_box.h) if vm_idx not in avail: raise ValueError(f'Recheck routing on layer {vm_layer}') rstlb_vm = self.connect_to_tracks(rstlb, TrackID(vm_layer, vm_idx, width=vm_w)) self.add_pin('rstlb_vm', rstlb_vm) self.sch_params = dict( lch=place_info.lch, seg_dict={'buf': seg_buf, 'in': seg_in, 'mux': seg_mux, 'keep': seg_keep, 'pass': seg_pass, 'rst': seg_rst, 'out': seg_out}, w_dict=dict(p_buf=wp_buf, n_buf=wn_buf, p_in=wp_in, n_in=wn_in, p_mux=wp_mux, n_mux=wn_mux, p_keep=wp_keep, n_keep=wn_keep, p_pass=wp_pass, n_pass=wn_pass, p_rst=wp_rst, n_rst=wn_rst, p_out=wp_out, n_out=wn_out), th_p=place_info.get_row_place_info(ridx_p).row_info.threshold, th_n=place_info.get_row_place_info(ridx_n).row_info.threshold, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) grid = self.grid tr_manager = self.tr_manager drv_params: Param = self.params['drv_params'] data_lv_params: Param = self.params['data_lv_params'] ctrl_lv_params: Param = self.params['ctrl_lv_params'] buf_data_lv_params: Param = self.params['buf_data_lv_params'] buf_ctrl_lv_params: Param = self.params['buf_ctrl_lv_params'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] rxhalf_ncol: int = self.params['rxhalf_ncol'] conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 # setup master parameters append = dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n) din_params = data_lv_params.copy(append=dict( dual_output=False, **append, )) itx_en_params = ctrl_lv_params.copy(append=dict( dual_output=True, **append, )) ctrl_params = itx_en_params.copy(append=dict(dual_output=False)) ndrv_params = ctrl_params.copy(append=dict(invert_out=True)) drv_params = drv_params.copy(append=append) # create masters lv_din_master = self.new_template(LevelShifterCoreOutBuffer, params=din_params) lv_itx_en_master = self.new_template(LevelShifterCoreOutBuffer, params=itx_en_params) lv_ndrv_master = self.new_template(LevelShifterCoreOutBuffer, params=ndrv_params) lv_ctrl_master = self.new_template(LevelShifterCoreOutBuffer, params=ctrl_params) drv_master: AIBOutputDriver = self.new_template(AIBOutputDriver, params=drv_params) buf_data_lv_master = self._get_buf_lv_master(buf_data_lv_params, append) buf_ctrl_lv_master = self._get_buf_lv_master(buf_ctrl_lv_params, append) # shift output tracks of weak_pullupenb, so it can cross into the other tile. tmp_tid = lv_ctrl_master.get_port('out').get_pins()[0].track_id pub_tidx = tr_manager.get_next_track(tmp_tid.layer_id, tmp_tid.base_index, 'sig', 'sig', up=True) pub_params = ctrl_params.copy(append=dict(sig_locs=dict(outr=pub_tidx))) lv_pub_master = self.new_template(LevelShifterCoreOutBuffer, params=pub_params) # track definitions bot_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=1) top_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=2) in_pins = ['ipdrv_buf<0>', 'itx_en_buf', 'weak_pulldownen', 'weak_pullupenb', 'indrv_buf<0>', 'indrv_buf<1>', 'din', 'ipdrv_buf<1>'] # Placement din_ncol = lv_din_master.num_cols ctrl_ncol = lv_ctrl_master.num_cols min_sep = self.min_sep_col sup_info = self.get_supply_column_info(xm_layer) # assume 2 inverter chains + margins have smaller width than a single lvl shifter if 2 * buf_data_lv_master.num_cols + min_sep > din_ncol: raise ValueError("input buffer too large compared to data level shifter's width") if 2 * buf_ctrl_lv_master.num_cols + min_sep > ctrl_ncol: raise ValueError("input buffer too large compared to control level shifter's width") # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_io_table = {lay: [] for lay in lay_range} vdd_core_table = {lay: [] for lay in lay_range} vss_table = {lay: [] for lay in lay_range} # instantiate cells and collect pins pin_dict = {'por': [], 'porb': [], 'VDDIO': [], 'VDD': [], 'VSS': []} cur_col = 0 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) # pdrv<0>, itx_en new_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ctrl_master, lv_itx_en_master, bot_vss, top_vss, in_pins[0], in_pins[1], True, True, False, pin_dict) ctrl_dual_ncol = new_col - cur_col cur_col = new_col cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, False) core_ncol = get_io_shifters_ncol(self.sub_sep_col, lv_ctrl_master, lv_pub_master) self._core_ncol = max(core_ncol, rxhalf_ncol) # puen, puenb draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ctrl_master, lv_pub_master, bot_vss, top_vss, in_pins[2], in_pins[3], False, False, False, pin_dict) cur_col += self._core_ncol + min_sep + self.sub_sep_col // 2 # ndrv<0>, ndrv<1> cur_col = draw_io_shifters(self, cur_col + self._core_ncol - core_ncol, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ndrv_master, lv_ndrv_master, bot_vss, top_vss, in_pins[4], in_pins[5], True, True, False, pin_dict) cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) # din, pdrv<1> din_ncol_tot = get_io_shifters_ncol(self.sub_sep_col, lv_din_master, lv_ctrl_master) din_ncol_max = max(din_ncol_tot, ctrl_dual_ncol) self._lvshift_right_ncol = din_ncol_max cur_col = draw_io_shifters(self, cur_col + din_ncol_max - din_ncol_tot, buf_data_lv_master, buf_ctrl_lv_master, lv_din_master, lv_ctrl_master, bot_vss, top_vss, in_pins[6], in_pins[7], True, True, True, pin_dict) drv_inst = self.add_tile(drv_master, 1, cur_col) # add tapes on bottom tile max_col = self._draw_bot_supply_column(cur_col, drv_master.tap_columns, sup_info, vdd_core_table, vss_table, ridx_p, ridx_n) self.set_mos_size(num_cols=max_col) # connect and export supply pins vss_list = vdd_io_list = vdd_core_list = [] for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd_io = vdd_io_table[lay] vss.extend(drv_inst.port_pins_iter(f'VSS_{lay}')) vdd_io.extend(drv_inst.port_pins_iter(f'VDD_{lay}')) vss_list = self.connect_wires(vss) vdd_io_list = self.connect_wires(vdd_io) vdd_core_list = self.connect_wires(vdd_core_table[lay], upper=vdd_io[0].upper) self.add_pin('VDDIO', vdd_io_list) self.add_pin('VDDCore', vdd_core_list) self.add_pin('VSS', vss_list) self.add_pin('VDDIO_vm', vdd_io_table[vm_layer], hide=True) self.add_pin('VDDCore_vm', vdd_core_table[vm_layer], hide=True) self.add_pin('VSS_vm', vss_table[vm_layer], hide=True) self.add_pin('VDDIO_conn', vdd_io_table[conn_layer], hide=True) self.add_pin('VDDCore_conn', vdd_core_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) self.reexport(drv_inst.get_port('VDD_vm'), net_name='VDDIO_vm') self.reexport(drv_inst.get_port('VSS_vm')) self.reexport(drv_inst.get_port('VDD_conn'), net_name='VDDIO_conn') self.reexport(drv_inst.get_port('VSS_conn')) # route input pins to the edge in0_tidx = tr_manager.get_next_track(xm_layer, vdd_core_list[0].track_id.base_index, 'sup', 'sig', up=True) in_tidx_list = tr_manager.place_wires(xm_layer, ['sig'] * len(in_pins), align_track=in0_tidx)[1] tr_xm_w = tr_manager.get_width(xm_layer, 'sig') vm_w = tr_manager.get_width(vm_layer, 'sig') for buf_idx, (tidx, name) in enumerate(zip(in_tidx_list, in_pins)): hm_warr = pin_dict[name][0] vm_tidx_ref = pin_dict[name + '_buf'][1].track_id.base_index vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx_ref, 'sig', 'sig', up=((buf_idx & 1) == 0)) vm_warr = self.connect_to_tracks(hm_warr, TrackID(vm_layer, vm_tidx, width=vm_w), min_len_mode=MinLenMode.LOWER) self.add_pin(name, self.connect_to_tracks(vm_warr, TrackID(xm_layer, tidx, width=tr_xm_w), track_lower=0), mode=PinMode.LOWER) # connect POR input # connect POR/POR_b por_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=2, tile_idx=2) porb_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=2, tile_idx=1) por = self.connect_to_tracks(pin_dict['por'], por_tid) porb = self.connect_to_tracks(pin_dict['porb'], porb_tid) self.add_pin('por_vccl', por) self.add_pin('porb_vccl', porb) # connect driver inputs # compute track indices # track order: weak_pden, n_enb_drv<0>, pad, p_en_drv<0>, weak_puenb, din # n_enb_drv<1>, tristateb, pad, tristate, p_en_drv<1> vdd_io_tidx = vdd_io_list[0].track_id.base_index pad_tr_w = tr_manager.get_width(xm_layer, 'padout') xm_hs_w = tr_manager.get_width(xm_layer, 'sig_hs') pad_0_tidx = grid.get_middle_track(vss_list[0].track_id[0].base_index, vdd_io_tidx, round_up=False) pad_1_tidx = grid.get_middle_track(vss_list[0].track_id[1].base_index, vdd_io_tidx, round_up=True) pad_2_tidx = grid.get_middle_track(vss_list[0].track_id[1].base_index, vdd_io_list[1].track_id.base_index, round_up=False) bb_tidx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'padout'], align_track=pad_0_tidx, align_idx=-1)[1] bt_tidx_list = tr_manager.place_wires(xm_layer, ['padout', 'sig', 'sig', 'sig_hs'], align_track=pad_0_tidx)[1] tb_tidx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'padout'], align_track=pad_1_tidx, align_idx=-1)[1] tt_tidx_list = tr_manager.place_wires(xm_layer, ['padout', 'sig', 'sig'], align_track=pad_1_tidx)[1] # connect wires self._connect_lv_drv(pin_dict, drv_inst, 'weak_pulldownen_out', 'weak_pden', TrackID(xm_layer, bb_tidx_list[0], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'indrv_buf<0>_out', 'n_enb_drv<0>', TrackID(xm_layer, bb_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'ipdrv_buf<0>_out', 'p_en_drv<0>', TrackID(xm_layer, bt_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'weak_pullupenb_out', 'weak_puenb', TrackID(xm_layer, bt_tidx_list[2], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'din_out', 'din', TrackID(xm_layer, bt_tidx_list[3], width=xm_hs_w)) self._connect_lv_drv(pin_dict, drv_inst, 'indrv_buf<1>_out', 'n_enb_drv<1>', TrackID(xm_layer, tb_tidx_list[0], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'itx_en_buf_out', 'tristateb', TrackID(xm_layer, tb_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'itx_en_buf_outb', 'tristate', TrackID(xm_layer, tt_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'ipdrv_buf<1>_out', 'p_en_drv<1>', TrackID(xm_layer, tt_tidx_list[2], width=tr_xm_w)) # connect driver outputs pad_pitch = pad_2_tidx - pad_0_tidx pad_tid0 = TrackID(xm_layer, pad_0_tidx, width=pad_tr_w, num=2, pitch=pad_pitch) pad_tid1 = TrackID(xm_layer, pad_1_tidx, width=pad_tr_w, num=2, pitch=pad_pitch) pad_pin = drv_inst.get_pin('txpadout') self.add_pin('txpadout', [self.connect_to_tracks(pad_pin, pad_tid0), self.connect_to_tracks(pad_pin, pad_tid1)]) self.add_pin('txpadout_vm', pad_pin, hide=True) # setup schematic parameters rm_keys = ['dual_output', 'invert_out'] data_lv_sch_params = lv_din_master.sch_params.copy(remove=rm_keys) ctrl_lv_sch_params = lv_ctrl_master.sch_params.copy(remove=rm_keys) self.sch_params = dict( drv_params=drv_master.sch_params, data_lv_params=dict( lev_params=data_lv_sch_params, buf_params=buf_data_lv_master.sch_params.copy(remove=['dual_output']), ), ctrl_lv_params=dict( lev_params=ctrl_lv_sch_params, buf_params=buf_ctrl_lv_master.sch_params.copy(remove=['dual_output']), ), )
def draw_layout(self): place_info = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(place_info) seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict'] ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] has_rstb: bool = self.params['has_rstb'] has_outbuf: bool = self.params['has_outbuf'] has_inbuf: bool = self.params['has_inbuf'] out_pitch: HalfInt = HalfInt.convert(self.params['out_pitch']) w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p, has_rstb) seg_fb = seg_dict['fb'] seg_ps = seg_dict['ps'] seg_nr = seg_dict['nr'] seg_obuf = seg_dict['obuf'] if has_outbuf else 0 seg_ibuf = seg_dict['ibuf'] if has_inbuf else 0 w_pfb = w_dict['pfb'] w_nfb = w_dict['nfb'] w_ps = w_dict['ps'] w_nr = w_dict['nr'] w_rst = w_dict.get('pr', 0) w_nbuf = w_nr w_pbuf = w_ps sch_seg_dict = dict(nfb=seg_fb, pfb=seg_fb, ps=seg_ps, nr=seg_nr) if has_rstb: sch_seg_dict['pr'] = seg_rst = seg_dict['rst'] else: seg_rst = 0 if seg_ps & 1 or seg_nr & 1 or seg_rst & 1 or seg_obuf & 1: raise ValueError( 'ps, nr, rst, and buf must have even number of segments') # placement min_sep = self.min_sep_col # use even step size to maintain supply conn_layer wires parity. min_sep += (min_sep & 1) hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 grid = self.grid arr_info = self.arr_info tr_manager = self.tr_manager hm_w = tr_manager.get_width(hm_layer, 'sig') vm_w = tr_manager.get_width(vm_layer, 'sig') hm_sep_col = self.get_hm_sp_le_sep_col(ntr=hm_w) mid_sep = max(hm_sep_col - 2, 0) mid_sep = (mid_sep + 1) // 2 if has_inbuf: m_nibuf = self.add_mos(ridx_n, mid_sep, seg_ibuf, w=w_nbuf) m_pibuf = self.add_mos(ridx_p, mid_sep, seg_ibuf, w=w_pbuf) cur_col = mid_sep + seg_ibuf psrb_list = [m_nibuf.g, m_pibuf.g] else: m_nibuf = m_pibuf = None cur_col = mid_sep psrb_list = [] nr_col = cur_col m_nr = self.add_mos(ridx_n, cur_col, seg_nr, w=w_nr) m_ps = self.add_mos(ridx_p, cur_col, seg_ps, w=w_ps) psrb_list.append(m_ps.g) pcol = cur_col + seg_ps if has_rstb: m_rst = self.add_mos(ridx_p, pcol, seg_rst, w=w_rst) pcol += seg_rst else: m_rst = None cur_col = max(cur_col + seg_nr, pcol) if has_outbuf: m_pinv = self.add_mos(ridx_p, cur_col, seg_obuf, w=w_pbuf) m_ninv = self.add_mos(ridx_n, cur_col, seg_obuf, w=w_nbuf) cur_col += seg_obuf else: m_pinv = m_ninv = None cur_col += min_sep fb_col = cur_col m_pfb = self.add_mos(ridx_p, cur_col, seg_fb, w=w_pfb, g_on_s=True, stack=2, sep_g=True) m_nfb = self.add_mos(ridx_n, cur_col, seg_fb, w=w_nfb, g_on_s=True, stack=2, sep_g=True) self.set_mos_size() # track planning vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nbuf_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-2) nq_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) psr_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1) pq_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pbuf_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sup') # try to spread out gate wires to lower parasitics on differential Q wires ng_lower = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) ng_upper = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) g_idx_list = tr_manager.spread_wires( hm_layer, ['sig', 'sig_hs', 'sig_hs', 'sig'], ng_lower, ng_upper, ('sig_hs', 'sig_hs'), alignment=0) self._q_tr_info = (hm_w, g_idx_list[2], g_idx_list[1]) self._sr_hm_tr_info = (hm_w, g_idx_list[3], g_idx_list[0]) if has_rstb: rst_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2) pq_conn_list = [m_ps.d, m_rst.d, m_pfb.s] vdd_list = [m_ps.s, m_rst.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] rstb = self.connect_to_tracks(m_rst.g, rst_tid, min_len_mode=MinLenMode.MIDDLE) rst_vm_tidx = grid.coord_to_track(vm_layer, rstb.middle, mode=RoundMode.GREATER_EQ) rstb_vm = self.connect_to_tracks( rstb, TrackID(vm_layer, rst_vm_tidx, width=vm_w)) self.add_pin('rstb', rstb_vm) else: pq_conn_list = [m_ps.d, m_pfb.s] vdd_list = [m_ps.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] self.add_pin('nsr', m_nr.g) self.add_pin('nsrb', m_nfb.g[0::2]) nq = self.connect_to_tracks([m_nr.d, m_nfb.s], nq_tid) pq = self.connect_to_tracks(pq_conn_list, pq_tid) psrb = self.connect_to_tracks(psrb_list, psr_tid, min_len_mode=MinLenMode.UPPER) psr = self.connect_to_tracks(m_pfb.g[0::2], psr_tid, min_len_mode=MinLenMode.LOWER) qb = self.connect_wires([m_nfb.g[1::2], m_pfb.g[1::2]]) self.add_pin('qb', qb) if has_inbuf: vdd_list.append(m_pibuf.s) vss_list.append(m_nibuf.s) nbuf = self.connect_to_tracks(m_nibuf.d, nbuf_tid, min_len_mode=MinLenMode.UPPER) pbuf = self.connect_to_tracks(m_pibuf.d, pbuf_tid, min_len_mode=MinLenMode.UPPER) vm_tidx = grid.coord_to_track(vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('sr_buf', buf) out_p_htr = out_pitch.dbl_value vm_ref = grid.coord_to_track(vm_layer, 0) srb_vm_tidx = arr_info.col_to_track(vm_layer, nr_col + 1, mode=RoundMode.GREATER_EQ) if has_outbuf: vdd_list.append(m_pinv.s) vss_list.append(m_ninv.s) nbuf = self.connect_to_tracks(m_ninv.d, nbuf_tid, min_len_mode=MinLenMode.MIDDLE) pbuf = self.connect_to_tracks(m_pinv.d, pbuf_tid, min_len_mode=MinLenMode.MIDDLE) vm_delta = grid.coord_to_track( vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr vm_tidx = vm_ref + HalfInt(vm_htr) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('buf_out', buf) buf_in = self.connect_wires([m_ninv.g, m_pinv.g]) self.add_pin('buf_in', buf_in) q_vm_tidx = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') else: vm_delta = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr q_vm_tidx = vm_ref + HalfInt(vm_htr) sr_vm_tidx = arr_info.col_to_track(vm_layer, fb_col, mode=RoundMode.LESS_EQ) self._sr_vm_tr_info = (vm_w, sr_vm_tidx, srb_vm_tidx) q_vm = self.connect_to_tracks([nq, pq], TrackID(vm_layer, q_vm_tidx, width=vm_w)) self.add_pin('q_vm', q_vm) self.add_pin('psr', psr) self.add_pin('psrb', psrb) self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss_list, vss_tid)) lch = arr_info.lch buf_params = ImmutableSortedDict( dict( lch=lch, w_p=w_pbuf, w_n=w_nbuf, th_p=th_dict['ps'], th_n=th_dict['nr'], seg=seg_obuf, )) obuf_params = buf_params if has_outbuf else None ibuf_params = buf_params.copy(append=dict( seg=seg_ibuf)) if has_inbuf else None self.sch_params = dict( core_params=ImmutableSortedDict( dict( lch=lch, seg_dict=ImmutableSortedDict(sch_seg_dict), w_dict=w_dict, th_dict=th_dict, )), outbuf_params=obuf_params, inbuf_params=ibuf_params, has_rstb=has_rstb, )
def draw_layout(self) -> None: master: SRLatchSymmetricHalf = self.new_template(SRLatchSymmetricHalf, params=self.params) self.draw_base(master.draw_base_info) swap_outbuf: bool = self.params['swap_outbuf'] hm_w, q_tidx, qb_tidx = master.q_tr_info _, sr_hm_top, sr_hm_bot = master.sr_hm_tr_info vm_w, sr_vm_tidx, srb_vm_tidx = master.sr_vm_tr_info # placement nhalf = master.num_cols corel = self.add_tile(master, 0, nhalf, flip_lr=True) corer = self.add_tile(master, 0, nhalf) self.set_mos_size(num_cols=2 * nhalf) hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 arr_info = self.arr_info vm0 = arr_info.col_to_track(vm_layer, 0) vmh = arr_info.col_to_track(vm_layer, nhalf) vmdr = vmh - vm0 vmdl = vmh + vm0 pr = corel.get_pin('psr') psb = corel.get_pin('psrb') nr = corel.get_pin('nsr') nsb = corel.get_pin('nsrb') ps = corer.get_pin('psr') prb = corer.get_pin('psrb') ns = corer.get_pin('nsr') nrb = corer.get_pin('nsrb') nr, nsb = self.connect_differential_tracks(nr, nsb, hm_layer, sr_hm_top, sr_hm_bot, width=hm_w) ns, nrb = self.connect_differential_tracks(ns, nrb, hm_layer, sr_hm_bot, sr_hm_top, width=hm_w) sb = self.connect_to_tracks([psb, nsb], TrackID(vm_layer, vmdl - srb_vm_tidx, width=vm_w)) r = self.connect_to_tracks([pr, nr], TrackID(vm_layer, vmdl - sr_vm_tidx, width=vm_w), track_lower=sb.lower) s = self.connect_to_tracks([ps, ns], TrackID(vm_layer, vmdr + sr_vm_tidx, width=vm_w)) rb = self.connect_to_tracks([prb, nrb], TrackID(vm_layer, vmdr + srb_vm_tidx, width=vm_w), track_lower=s.lower) self.add_pin('sb', sb) self.add_pin('rb', rb) if corel.has_port('sr_buf'): sbuf = corel.get_pin('sr_buf') rbuf = corer.get_pin('sr_buf') self.connect_to_tracks(sbuf, ns.track_id, track_upper=ns.upper) self.connect_to_tracks(rbuf, nr.track_id, track_lower=nr.lower) else: self.add_pin('s', s) self.add_pin('r', r) q_list = [corel.get_pin('q_vm'), corer.get_pin('qb')] qb_list = [corer.get_pin('q_vm'), corel.get_pin('qb')] if corel.has_port('buf_out'): if swap_outbuf: self.reexport(corel.get_port('buf_out'), net_name='qb') self.reexport(corer.get_port('buf_out'), net_name='q') q_list.append(corel.get_pin('buf_in')) qb_list.append(corer.get_pin('buf_in')) else: self.reexport(corel.get_port('buf_out'), net_name='q') self.reexport(corer.get_port('buf_out'), net_name='qb') q_list.append(corer.get_pin('buf_in')) qb_list.append(corel.get_pin('buf_in')) else: self.add_pin('q', q_list[0]) self.add_pin('qb', qb_list[0]) self.connect_differential_tracks(q_list, qb_list, self.conn_layer + 1, q_tidx, qb_tidx, width=hm_w) if corel.has_port('rstb'): self.reexport(corel.get_port('rstb'), net_name='rsthb') self.reexport(corer.get_port('rstb'), net_name='rstlb') self.add_pin( 'VDD', self.connect_wires([corel.get_pin('VDD'), corer.get_pin('VDD')])) self.add_pin( 'VSS', self.connect_wires([corel.get_pin('VSS'), corer.get_pin('VSS')])) self.sch_params = master.sch_params
def join_bias_vroutes(template, vm_layer, vdd_dx, vss_dx, xr, num_vdd_tot, num_vss_tot, hm_bias_info_list, bias_config, vdd_pins, vss_pins, xl=0, yt=None, vss_warrs=None, vdd_warrs=None): grid = template.grid vss_intvs = IntervalSet() vdd_intvs = IntervalSet() if vss_warrs is not None: for w in WireArray.single_warr_iter(vss_warrs): vss_intvs.add(w.track_id.get_bounds(grid, unit_mode=True), val=w) if vdd_warrs is not None: for w in WireArray.single_warr_iter(vdd_warrs): vdd_intvs.add(w.track_id.get_bounds(grid, unit_mode=True), val=w) vdd_xl, vdd_xr = vdd_dx vss_xl, vss_xr = vss_dx vdd_xl += xl vdd_xr += xl vss_xl += xl vss_xr += xl vss_params = dict( nwire=num_vss_tot, width=1, space_sig=0, ) vdd_params = dict( nwire=num_vdd_tot, width=1, space_sig=0, ) hm_layer = vm_layer + 1 vss_hm_list = [] vdd_hm_list = [] inst_info_list = [] vss_y_prev = vdd_y_prev = None params = dict( bot_layer=vm_layer, bias_config=bias_config, ) for idx, (code, num, y0) in enumerate(hm_bias_info_list): if code == 0: params['bot_params'] = vss_params if vss_y_prev is not None and y0 > vss_y_prev: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, y0, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) else: params['bot_params'] = vdd_params if vdd_y_prev is not None and y0 > vdd_y_prev: sup_warrs = list(vdd_intvs.overlap_values((vdd_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vdd_tot, vdd_xl, vdd_y_prev, y0, sup_warrs=sup_warrs) vdd_hm_list.extend(tmp[0]) if vss_y_prev is not None and y0 > vss_y_prev: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, y0))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, y0, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) params['top_params'] = dict( nwire=num, width=1, space_sig=0, ) if idx == 0: master = template.new_template(params=params, temp_cls=BiasShieldJoin) if code == 1: inst_info_list.append((1, master, vdd_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vdd_xr, xr) vdd_y_prev = y0 + master.array_box.top_unit else: inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit elif code == 1: params['bot_open'] = True master = template.new_template(params=params, temp_cls=BiasShieldJoin) inst_info_list.append((1, master, vdd_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vdd_xr, vss_xl, tb_mode=1) vdd_y_prev = y0 + master.array_box.top_unit params['draw_top'] = False params['bot_params'] = vss_params master = template.new_template(params=params, temp_cls=BiasShieldCrossing) inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit else: params['bot_open'] = True master = template.new_template(params=params, temp_cls=BiasShieldJoin) inst_info_list.append((0, master, vss_xl, y0)) BiasShield.draw_bias_shields(template, hm_layer, bias_config, num, y0, vss_xr, xr) vss_y_prev = y0 + master.array_box.top_unit if yt is None: yt = max(vss_y_prev, vdd_y_prev) if vss_y_prev < yt: sup_warrs = list(vss_intvs.overlap_values((vss_y_prev, yt))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vss_tot, vss_xl, vss_y_prev, yt, sup_warrs=sup_warrs) vss_hm_list.extend(tmp[0]) if vdd_y_prev < yt: sup_warrs = list(vdd_intvs.overlap_values((vdd_y_prev, yt))) tmp = BiasShield.draw_bias_shields(template, vm_layer, bias_config, num_vdd_tot, vdd_xl, vdd_y_prev, yt, sup_warrs=sup_warrs) vdd_hm_list.extend(tmp[0]) sup_layer = hm_layer + 1 vss_list = [] vdd_list = [] for code, master, x0, y0 in inst_info_list: inst = _add_inst_r180(template, master, x0, y0) if code == 1: vdd_list.extend(inst.port_pins_iter('sup', layer=sup_layer)) else: vss_list.extend(inst.port_pins_iter('sup', layer=sup_layer)) vdd_list = template.connect_wires(vdd_list, upper=yt, unit_mode=True) vss_list = template.connect_wires(vss_list, upper=yt, unit_mode=True) template.draw_vias_on_intersections(vdd_hm_list, vdd_list) template.draw_vias_on_intersections(vss_hm_list, vss_list) # connect pins vss_tidx_list = BiasShield.get_route_tids(grid, vm_layer, vss_xl, bias_config, num_vss_tot) vdd_tidx_list = BiasShield.get_route_tids(grid, vm_layer, vdd_xl, bias_config, num_vdd_tot) pin_map = {} vss_tr_warrs = [] vdd_tr_warrs = [] for pins, tidx_list, result_list in ((vss_pins, vss_tidx_list, vss_tr_warrs), (vdd_pins, vdd_tidx_list, vdd_tr_warrs)): next_idx = 0 for name, warr in pins: if name in pin_map: cur_idx = pin_map[name] add_pin = False else: cur_idx = pin_map[name] = next_idx next_idx += 1 add_pin = True tidx, tr_w = tidx_list[cur_idx + 1] tid = TrackID(vm_layer, tidx, width=tr_w) warr = template.connect_to_tracks(warr, tid, track_upper=yt, unit_mode=True) if add_pin: result_list.append((name, warr)) pin_map.clear() return vdd_tr_warrs, vss_tr_warrs, vdd_list, vss_list
def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] wp = self.params['wp'] wn = self.params['wn'] thp = self.params['thp'] thn = self.params['thn'] seg_list = self.params['seg_list'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] out_tid = self.params['out_tid'] guard_ring_nf = self.params['guard_ring_nf'] show_pins = self.params['show_pins'] # get AnalogBaseInfo hm_layer = self.mos_conn_layer + 1 layout_info = AnalogBaseInfo(self.grid, lch, guard_ring_nf, top_layer=hm_layer) fg_sep = layout_info.min_fg_sep fg_tot = sum(seg_list) + fg_sep * (len(seg_list) - 1) # construct track width/space dictionary from EM specs tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) nw_list = [wn] pw_list = [wp] nth_list = [thn] pth_list = [thp] wire_dict_list = [dict(g=['out'])] wire_names = dict(nch=wire_dict_list, pch=wire_dict_list) # draw transistor rows 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=['MX'], p_orientations=['R0'], guard_ring_nf=guard_ring_nf, pgr_w=ptap_w, ngr_w=ntap_w, top_layer=hm_layer) ng_tid = self.get_wire_id('nch', 0, 'g', wire_idx=0) pg_tid = self.get_wire_id('pch', 0, 'g', wire_idx=0) num = len(seg_list) if out_tid is None: in0_tid = ng_tid out0_tid = pg_tid else: out0_tid = TrackID(hm_layer, out_tid[0], width=out_tid[1]) if abs(pg_tid.base_index - out_tid[0]) < abs(ng_tid.base_index - out_tid[0]): in0_tid = ng_tid else: in0_tid = pg_tid if num % 2 == 0: tmp = in0_tid in0_tid = out0_tid out0_tid = tmp col = 0 prev_out = None for idx, seg in enumerate(seg_list): out_name = 'out' if idx == num - 1 else 'mid%d' % idx pmos = self.draw_mos_conn('pch', 0, col, seg, 2, 0, d_net=out_name) nmos = self.draw_mos_conn('nch', 0, col, seg, 0, 2, d_net=out_name) self.connect_to_substrate('ptap', nmos['s']) self.connect_to_substrate('ntap', pmos['s']) if idx % 2 == 0: in_tid = in0_tid out_tid = out0_tid else: in_tid = out0_tid out_tid = in0_tid if prev_out is None: w_in = self.connect_to_tracks([nmos['g'], pmos['g']], in_tid) self.add_pin('in', w_in, show=show_pins) else: self.connect_to_track_wires([nmos['g'], pmos['g']], prev_out) prev_out = self.connect_to_tracks([nmos['d'], pmos['d']], out_tid) col += seg + fg_sep sup_tr_w = tr_manager.get_width(hm_layer, 'sup') vss, vdd = self.fill_dummy(vdd_width=sup_tr_w, vss_width=sup_tr_w) self.add_pin('VSS', vss, show=show_pins) self.add_pin('VDD', vdd, show=show_pins) self.add_pin('out', prev_out, show=show_pins) # get schematic parameters num_seg = len(seg_list) self._sch_params = dict( lch=lch, wp_list=[wp] * num_seg, wn_list=[wn] * num_seg, thp=thp, thn=thn, segp_list=seg_list, segn_list=seg_list, dum_info=self.get_sch_dummy_info(), )
def _draw_data_path(self, anchor: int, se_master: MOSBase, lv_data_master: MOSBase, inv_master: MOSBase, vss_tid: TrackID, flip_lr: bool, prefix: str, pin_dict: Dict[str, List[WireArray]]) -> None: tr_manager = self.tr_manager grid = self.grid vm_layer = self.conn_layer + 2 dir_sign = 1 - 2 * int(flip_lr) inv_ncol = inv_master.num_cols inv_ncol += (inv_ncol & 1) inv = self.add_tile(inv_master, 0, anchor + dir_sign * inv_ncol, flip_lr=not flip_lr) se = self.add_tile(se_master, 1, anchor, flip_lr=flip_lr) min_sep = self.min_sep_col min_sep += (min_sep & 1) if flip_lr: lv_col = anchor - inv_ncol - min_sep else: lv_col = anchor + inv_ncol + min_sep lv = self.add_tile(lv_data_master, 0, lv_col, flip_lr=flip_lr) # figure out wire location for connecting data buffer to level shifter test_pin = se.get_pin('outp') vm_test = lv.get_pin('midl') test_coord = test_pin.lower if flip_lr else test_pin.upper vm_w = tr_manager.get_width(vm_layer, 'sig') test_tidx = tr_manager.get_next_track(vm_layer, vm_test.track_id.base_index, 'sig', 'sig', up=-2 * dir_sign) test_wbnd = grid.get_wire_bounds(vm_layer, test_tidx, vm_w)[flip_lr] if (((test_wbnd < test_coord) and not flip_lr) or ((test_wbnd > test_coord) and flip_lr)): # closest wires don't work vm_ref = lv.get_pin('midr') tidx_p = tr_manager.get_next_track(vm_layer, vm_ref.track_id.base_index, 'sig', 'sig', up=dir_sign) else: tidx_p = test_tidx tidx_n = tr_manager.get_next_track(vm_layer, tidx_p, 'sig', 'sig', up=dir_sign) outp, outn = self.connect_differential_tracks(se.get_all_port_pins('outp'), se.get_all_port_pins('outn'), vm_layer, tidx_p, tidx_n, width=vm_w) self.connect_differential_wires(outp, outn, lv.get_pin('in'), lv.get_pin('inb')) self.connect_to_tracks(lv.get_pin('rst_outb'), vss_tid) pin_dict[prefix + 'en'] = [se.get_pin('en')] pin_dict[prefix + 'enb'] = [se.get_pin('enb')] pin_dict['por_core'].append(lv.get_pin('rst_out')) pin_dict['porb_core'].append(lv.get_pin('rst_casc')) vm_tidx_ref = inv.get_pin('out').track_id.base_index in_vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx_ref, 'sig', 'sig', up=False) in_vm = self.connect_to_tracks(inv.get_pin('in'), TrackID(vm_layer, in_vm_tidx, width=vm_w)) if se.has_port('inp'): # clock path pin_dict[prefix + 'inp'] = [se.get_pin('inp')] pin_dict[prefix + 'inn'] = [se.get_pin('inn')] pin_dict[prefix + 'outp'] = [lv.get_pin('out')] pin_dict[prefix + 'outn'] = [lv.get_pin('outb')] self.connect_to_tracks(in_vm, vss_tid) else: # data path pin_dict[prefix + 'in'] = [se.get_pin('in')] pin_dict[prefix + 'out'] = [lv.get_pin('out')] pin_dict[prefix + 'async'] = [inv.get_pin('out')] self.connect_to_track_wires([lv.get_pin('poutb'), lv.get_pin('noutb')], in_vm)
def draw_layout(self) -> None: ndio_cls_name: str = self.params['ndio_cls'] pdio_cls_name: str = self.params['pdio_cls'] ndio_params: Param = self.params['ndio_params'] pdio_params: Param = self.params['pdio_params'] fe_params: Param = self.params['fe_params'] npadout: int = self.params['npadout'] w_dio_min: int = self.params['w_dio_min'] dio_margin: int = self.params['dio_margin'] tb_margin: int = self.params['tb_margin'] fe_ip_params = dict( cls_name='xbase.layout.mos.top.GenericWrapper', params=dict( cls_name=Frontend.get_qualified_name(), params=fe_params, export_hidden=True, half_blk_x=False, ), ) # make masters fe_master: TemplateBase = self.new_template(IPMarginTemplate, params=fe_ip_params) ndio_cls = cast(Type[TemplateBase], import_class(ndio_cls_name)) pdio_cls = cast(Type[TemplateBase], import_class(pdio_cls_name)) nd_master: TemplateBase = self.new_template(ndio_cls, params=ndio_params) pd_master: TemplateBase = self.new_template(pdio_cls, params=pdio_params) # floorplan grid = self.grid top_layer = nd_master.top_layer w_blk, h_blk = grid.get_block_size(top_layer) tb_margin = -(-tb_margin // h_blk) * h_blk fe_box = fe_master.bound_box nd_box = nd_master.bound_box pd_box = pd_master.bound_box h_fe = fe_box.h h_nd = nd_box.h h_pd = pd_box.h w_fe = fe_box.w w_nd = nd_box.w w_pd = pd_box.w w_dio = max(w_dio_min + dio_margin, w_nd + w_pd) w_tot = w_fe + w_dio w_tot = -(-w_tot // w_blk) * w_blk h_tot = max(h_fe, h_nd, h_pd) + 2 * tb_margin y_fe = (h_tot - h_fe) // (2 * h_blk) * h_blk y_nd = (h_tot - h_nd) // (2 * h_blk) * h_blk y_pd = (h_tot - h_pd) // (2 * h_blk) * h_blk x_nd = w_fe x_pd = w_tot - w_pd # instantiate blocks fe = self.add_instance(fe_master, inst_name='XFE', xform=Transform(0, y_fe)) nd = self.add_instance(nd_master, inst_name='XND', xform=Transform(x_nd, y_nd)) pd = self.add_instance(pd_master, inst_name='XPD', xform=Transform(x_pd, y_pd)) self.set_size_from_bound_box(top_layer, BBox(0, 0, w_tot, h_tot)) # diode guard ring connections vlay = (fe.get_port('VSS').get_single_layer(), 'drawing') vdir = Direction.LOWER vss_list = [] vdd_list = [] vddio_list = [] vss_bbox: List[BBox] = fe.get_all_port_pins('VSS', layer=vlay[0]) vdd_bbox: List[BBox] = fe.get_all_port_pins('VDDCore', layer=vlay[0]) vddio_bbox: List[BBox] = fe.get_all_port_pins('VDDIO', layer=vlay[0]) vss_pd = pd.get_all_port_pins('gr') for bbox in vss_bbox: vss_list.extend(self.connect_bbox_to_track_wires(vdir, vlay, bbox, vss_pd)) vss_list = self.connect_wires(vss_list) vddio_nd = nd.get_all_port_pins('gr') for bbox in vddio_bbox: vddio_list.extend(self.connect_bbox_to_track_wires(vdir, vlay, bbox, vddio_nd)) vddio_list = self.connect_wires(vddio_list) # diode ports vddio_pd = pd.get_all_port_pins('sub') vss_nd = nd.get_all_port_pins('sub') dio_sp_le = grid.get_line_end_space(vss_nd[0].layer_id, vss_nd[0].track_id.width, even=True) dio_sp_le2 = dio_sp_le // 2 if dio_margin < 0: dio_margin = dio_sp_le2 dio_port_xh = w_tot - dio_margin x_dio_mid = (nd.bound_box.xh + pd.bound_box.xl) // 2 vddio_pd = self.extend_wires(vddio_pd, lower=x_dio_mid + dio_sp_le2, upper=dio_port_xh) vss_nd = self.extend_wires(vss_nd, lower=x_nd, upper=x_dio_mid - dio_sp_le2) self.add_pin('VDDIO', vddio_pd) self.add_pin('VSS', vss_nd) pad = pd.get_all_port_pins('pad') pad.extend(nd.port_pins_iter('pad')) pad = self.connect_wires(pad, lower=x_nd, upper=dio_port_xh)[0] self.add_pin('iopad', pad) # short diode wires so we are LVS clean ym_layer = vddio_nd[0].layer_id - 1 vss_tidx = grid.coord_to_track(ym_layer, x_nd + w_nd, mode=RoundMode.GREATER_EQ) vddio_tidx = grid.coord_to_track(ym_layer, x_pd, mode=RoundMode.LESS_EQ) iopad_tidx = grid.get_middle_track(vss_tidx, vddio_tidx, round_up=False) ym_w = grid.get_min_track_width(ym_layer, top_ntr=vddio_nd[0].track_id.width) vddio_pd.extend(vddio_list) vss_nd.extend(vss_list) self.connect_to_tracks(vddio_pd, TrackID(ym_layer, vddio_tidx, width=ym_w)) self.connect_to_tracks(vss_nd, TrackID(ym_layer, vss_tidx, width=ym_w)) self.connect_to_tracks(pad, TrackID(ym_layer, iopad_tidx, width=ym_w)) # connect iopad and iclkn npad = pad.track_id.num pad_conn = pad[:npadout] iclkn_bnds = [COORD_MAX, COORD_MIN] iopad_out_list = [] for bbox in fe.port_pins_iter('txpadout', layer=vlay[0]): warr = self.connect_bbox_to_track_wires(vdir, vlay, bbox, pad_conn) iopad_out_list.append(warr) for bbox in fe.port_pins_iter('rxpadin', layer=vlay[0]): cur_bnds = [0, 0] warr = self.connect_bbox_to_track_wires(vdir, vlay, bbox, pad_conn, ret_bnds=cur_bnds) iopad_out_list.append(warr) iclkn_bnds[0] = min(iclkn_bnds[0], cur_bnds[0]) iclkn_bnds[1] = max(iclkn_bnds[1], cur_bnds[1]) # export iopad_out pad_conn_tid = pad_conn.track_id pad_layer = pad_conn_tid.layer_id iopad_out_list = self.connect_wires(iopad_out_list) lower = iopad_out_list[0].lower pad_bnds = grid.get_wire_bounds(pad_layer, pad_conn_tid.base_index, width=pad_conn_tid.width) pin_len = grid.get_next_length(pad_layer, pad_conn_tid.width, pad_bnds[1] - pad_bnds[0], even=True) ms_params = self.add_res_metal_warr(pad_layer, pad_conn_tid.base_index, lower + pin_len, lower + 2 * pin_len, width=pad_conn_tid.width, num=pad_conn_tid.num, pitch=pad_conn_tid.pitch) self.add_pin('iopad_out', WireArray(pad_conn_tid, lower, lower + pin_len)) bbox_iclkn = fe.get_pin('iclkn', layer=vlay[0]) bbox_iclkn = bbox_iclkn.set_interval(Orient2D.y, iclkn_bnds[0], iclkn_bnds[1]) # NOTE: some process require metal to be in same hierarchy as pin self.add_rect(vlay, bbox_iclkn) self.add_pin_primitive('iclkn', vlay[0], bbox_iclkn) # supply connections for idx in range(npad): tid = vss_nd[idx].track_id if idx == 0 or idx == npad - 1: cur_list = vdd_list cur_bbox = vdd_bbox elif idx & 1: cur_list = vss_list cur_bbox = vss_bbox else: cur_list = vddio_list cur_bbox = vddio_bbox for bbox in cur_bbox: cur_list.append(self.connect_bbox_to_tracks(vdir, vlay, bbox, tid)) self.add_pin('VDDCore', self.connect_wires(vdd_list)) self.add_pin('VDDIO', self.connect_wires(vddio_list)) self.add_pin('VSS', self.connect_wires(vss_list)) for name in ['VDDCore', 'VDDIO', 'VSS']: self.reexport(fe.get_port(f'{name}_xm'), net_name=name, hide=False) # reexport pins for name in ['din', 'ipdrv_buf<1>', 'ipdrv_buf<0>', 'indrv_buf<1>', 'indrv_buf<0>', 'itx_en_buf', 'weak_pulldownen', 'weak_pullupenb']: self.reexport(fe.get_port(name)) for name in ['clk_en', 'data_en', 'por', 'odat', 'odat_async', 'oclkp', 'oclkn']: self.reexport(fe.get_port(name)) self.sch_params = dict( fe_params=fe_master.sch_params, nd_params=nd_master.sch_params, pd_params=pd_master.sch_params, ms_params=ms_params, )
def draw_layout(self): place_info = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(place_info) seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict'] ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] has_rstb: bool = self.params['has_rstb'] has_bridge: bool = self.params['has_bridge'] vertical_out: bool = self.params['vertical_out'] vertical_rstb: bool = self.params['vertical_rstb'] w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p, has_bridge) seg_in = seg_dict['in'] seg_tail = seg_dict['tail'] seg_nfb = seg_dict['nfb'] seg_pfb = seg_dict['pfb'] seg_swm = seg_dict['sw'] w_in = w_dict['in'] w_tail = w_dict['tail'] w_nfb = w_dict['nfb'] w_pfb = w_dict['pfb'] if seg_in & 1 or (seg_tail % 4 != 0) or seg_nfb & 1 or seg_pfb & 1: raise ValueError( 'in, tail, nfb, or pfb must have even number of segments') # NOTE: make seg_swo even so we can abut transistors seg_swo = seg_swm + (seg_swm & 1) seg_tail = seg_tail // 2 # placement ridx_in = ridx_n + 1 ridx_nfb = ridx_in + 1 m_in = self.add_mos(ridx_in, 0, seg_in, w=w_in) m_nfb = self.add_mos(ridx_nfb, 0, seg_nfb, w=w_nfb) m_pfb = self.add_mos(ridx_p, 0, seg_pfb, w=w_pfb) ng_tid = self.get_track_id(ridx_nfb, MOSWireType.G, wire_name='sig', wire_idx=-1) mid_tid = self.get_track_id(ridx_nfb, MOSWireType.DS, wire_name='sig') pg_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig') vdd_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sup') pclk_tid = pg_tid if has_rstb: vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nclk_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) nrst_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1) prst_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1) tail_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='tail') tail_in_tid = self.get_track_id(ridx_in, MOSWireType.DS, wire_name='sig') m_tail = self.add_mos(ridx_n, 0, seg_tail, w=w_tail, g_on_s=True, stack=2, sep_g=True) m_swo_rst = self.add_mos(ridx_p, seg_pfb, seg_swo, w=w_pfb) m_swo = self.add_mos(ridx_p, seg_pfb + seg_swo, seg_swo, w=w_pfb) m_swm = self.add_mos(ridx_p, seg_pfb + 2 * seg_swo, seg_swm, w=w_pfb) vss_conn = m_tail.s tail_conn = m_tail.d g_conn = m_tail.g rstb_conn = g_conn[0::2] clk_conn = g_conn[1::2] else: vss_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sup') nclk_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) nrst_tid = prst_tid = None tail_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig') tail_in_tid = tail_tid m_tail = self.add_mos(ridx_n, 0, seg_tail, w=w_tail) m_swo = self.add_mos(ridx_p, seg_pfb, seg_swo, w=w_pfb) m_swm = self.add_mos(ridx_p, seg_pfb + seg_swo, seg_swm, w=w_pfb) m_swo_rst = None vss_conn = m_tail.s tail_conn = m_tail.d rstb_conn = None clk_conn = m_tail.g # NOTE: force even number of columns to make sure VDD conn_layer wires are on even columns. ncol_tot = self.num_cols self.set_mos_size(num_cols=ncol_tot + (ncol_tot & 1)) # routing conn_layer = self.conn_layer vm_layer = conn_layer + 2 vm_w = self.tr_manager.get_width(vm_layer, 'sig') grid = self.grid if has_rstb: nrst = self.connect_to_tracks(rstb_conn, nrst_tid) prst = self.connect_to_tracks([m_swo_rst.g], prst_tid) self.add_pin('nrstb', nrst) self.add_pin('prstb', prst) if vertical_rstb: xrst = grid.track_to_coord(conn_layer, m_swo_rst.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xrst, mode=RoundMode.GREATER_EQ) self.connect_to_tracks([nrst, prst], TrackID(vm_layer, vm_tidx, width=vm_w)) tail = self.connect_to_tracks(tail_conn, tail_tid) in_d = m_in.d tail_in = self.connect_to_tracks(in_d, tail_in_tid) self.add_pin('tail_in', tail_in) tail_list = [tail, tail_in] for warr in in_d.warr_iter(): xwire = grid.track_to_coord(conn_layer, warr.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xwire, mode=RoundMode.GREATER_EQ) self.connect_to_tracks(tail_list, TrackID(vm_layer, vm_tidx, width=vm_w)) out = self.connect_wires([m_nfb.d, m_pfb.d, m_swo.d, m_swo_rst.d]) vdd = self.connect_to_tracks( [m_pfb.s, m_swo.s, m_swm.s, m_swo_rst.s], vdd_tid) mid = self.connect_to_tracks([m_in.s, m_nfb.s, m_swm.d], mid_tid) else: tail = self.connect_to_tracks([tail_conn, m_in.d], tail_tid) out = self.connect_wires([m_nfb.d, m_pfb.d, m_swo.d]) vdd = self.connect_to_tracks([m_pfb.s, m_swo.s, m_swm.s], vdd_tid) mid = self.connect_to_tracks([m_in.s, m_nfb.s, m_swm.d], mid_tid) vss = self.connect_to_tracks(vss_conn, vss_tid) nclk = self.connect_to_tracks(clk_conn, nclk_tid) nout = self.connect_to_tracks(m_nfb.g, ng_tid) pout = self.connect_to_tracks(m_pfb.g, pg_tid) pclk = self.connect_to_tracks([m_swo.g, m_swm.g], pclk_tid) xclk = grid.track_to_coord(conn_layer, m_swo.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xclk, mode=RoundMode.GREATER_EQ) clk_vm = self.connect_to_tracks([nclk, pclk], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('clk_vm', clk_vm) xout = grid.track_to_coord(conn_layer, m_pfb.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xout, mode=RoundMode.GREATER_EQ) if vertical_out: out_vm = self.connect_to_tracks([nout, pout], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('out_vm', out_vm) else: self.add_pin('pout', pout) self.add_pin('nout', nout) self.add_pin('VSS', vss) self.add_pin('VDD', vdd) self.add_pin('tail', tail) self.add_pin('clk', nclk) self.add_pin('in', m_in.g) self.add_pin('out', out) self.add_pin('mid', mid) append_dict = dict(swo=seg_swo, swm=seg_swm) if has_bridge: append_dict['br'] = 1 sch_seg_dict = seg_dict.copy(append=append_dict, remove=['sw']) self.sch_params = dict( lch=self.arr_info.lch, seg_dict=sch_seg_dict, w_dict=w_dict, th_dict=th_dict, has_rstb=has_rstb, has_bridge=has_bridge, )
def draw_layout(self): grid = self.grid params = self.params pinfo = MOSBasePlaceInfo.make_place_info(grid, params['pinfo']) self.draw_base(pinfo) tr_manager = self.tr_manager seg_p: int = params['seg_p'] seg_n: int = params['seg_n'] seg_nand: int = params['seg_nand'] seg_nor: int = params['seg_nor'] w_p: int = params['w_p'] w_n: int = params['w_n'] w_p_nand: int = params['w_p_nand'] w_n_nand: int = params['w_n_nand'] w_p_nor: int = params['w_p_nor'] w_n_nor: int = params['w_n_nor'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] export_pins: bool = params['export_pins'] vm_layer = self.conn_layer + 2 #if (seg_p - seg_n) % 2 != 0: # raise ValueError('seg_p - seg_n should be even') in0_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) nout_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0) pout_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=-1) pin_drv_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0) nin_drv_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2) sig_locs_gate = dict(nin=in0_tidx, nout=nout_tidx, pout=pout_tidx) sig_locs_drv = dict(pin=pin_drv_tidx, nin=nin_drv_tidx) nand_params = dict( pinfo=pinfo, seg=seg_nand, vertical_out=False, vertical_in=False, is_guarded=True, w_p=w_p_nand, w_n=w_n_nand, ridx_n=ridx_n, ridx_p=ridx_p, sig_locs=sig_locs_gate, min_len_mode=dict( in0=MinLenMode.LOWER, in1=MinLenMode.LOWER, ), ) nor_params = nand_params.copy() nor_params['seg'] = seg_nor nor_params['w_p'] = w_p_nor nor_params['w_n'] = w_n_nor drv_params = dict( pinfo=pinfo, seg_p=seg_p, seg_n=seg_n, w_p=w_p, w_n=w_n, stack=1, ridx_p=ridx_p, ridx_n=ridx_n, sig_locs=sig_locs_drv, ) # --- Placement --- # nand_master = self.new_template(NAND2Core, params=nand_params) nor_master = self.new_template(NOR2Core, params=nor_params) drv_master = self.new_template(PullUpDown, params=drv_params) nand_ncols = nand_master.num_cols nor_ncols = nor_master.num_cols drv_ncols = drv_master.num_cols min_sep = self.min_sep_col ncol_tot = nand_ncols + drv_ncols + nor_ncols + 2 * min_sep nand_inst = self.add_tile(nand_master, 0, nand_ncols, flip_lr=True) drv_inst = self.add_tile(drv_master, 0, nand_ncols + min_sep) nor_inst = self.add_tile(nor_master, 0, ncol_tot - nor_ncols) self.set_mos_size(num_cols=ncol_tot) # Routing pin = self.connect_wires( [nor_inst.get_pin('pin<0>'), nand_inst.get_pin('pin<0>')]) nin = self.connect_wires( [nor_inst.get_pin('nin<0>'), nand_inst.get_pin('nin<0>')]) self.add_pin('pin', pin, label='in:') self.add_pin('nin', nin, label='in:') bnd_box = self.bound_box vm_tr_w = tr_manager.get_width(vm_layer, 'sig') en_vm_tidx = grid.coord_to_track(vm_layer, bnd_box.xl, mode=RoundMode.GREATER_EQ) enb_vm_tidx = grid.coord_to_track(vm_layer, bnd_box.xh, mode=RoundMode.LESS_EQ) puenb_vm_tidx = tr_manager.get_next_track(vm_layer, en_vm_tidx, 'sig', 'sig', up=True) pden_vm_tidx = tr_manager.get_next_track(vm_layer, enb_vm_tidx, 'sig', 'sig', up=False) puenb = drv_inst.get_pin('puenb') pden = drv_inst.get_pin('pden') if export_pins: self.add_pin('nand_pu', puenb) self.add_pin('nor_pd', pden) self.connect_to_tracks( [nand_inst.get_pin('pout'), nand_inst.get_pin('nout'), puenb], TrackID(vm_layer, puenb_vm_tidx, width=vm_tr_w)) self.connect_to_tracks( [nor_inst.get_pin('pout'), nor_inst.get_pin('nout'), pden], TrackID(vm_layer, pden_vm_tidx, width=vm_tr_w)) en = self.connect_to_tracks( [nand_inst.get_pin('pin<1>'), nand_inst.get_pin('nin<1>')], TrackID(vm_layer, en_vm_tidx, width=vm_tr_w)) enb = self.connect_to_tracks( [nor_inst.get_pin('pin<1>'), nor_inst.get_pin('nin<1>')], TrackID(vm_layer, enb_vm_tidx, width=vm_tr_w)) self.add_pin('en', en) self.add_pin('enb', enb) self.reexport(drv_inst.get_port('out')) self.reexport(drv_inst.get_port('pout')) self.reexport(drv_inst.get_port('nout')) # connect vss and vdd vdd = self.connect_wires([ nand_inst.get_pin('VDD'), nor_inst.get_pin('VDD'), drv_inst.get_pin('VDD') ]) vss = self.connect_wires([ nand_inst.get_pin('VSS'), nor_inst.get_pin('VSS'), drv_inst.get_pin('VSS') ]) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # get schematic parameters self.sch_params = dict( nand_params=nand_master.sch_params, nor_params=nor_master.sch_params, pupd_params=drv_master.sch_params, export_pins=export_pins, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] core_ncol: int = self.params['core_ncol'] tap_info_list: ImmutableList[Tuple[int, bool]] = self.params['tap_info_list'] tmp = self._make_masters(self, pinfo, self.params) (lv_data_master, lv_ctrl_master, lv_por_master, inv_master, data_master, clk_master, buf_ctrl_lv_master, buf_por_lv_master) = tmp inbuf_ncol = max(data_master.num_cols, clk_master.num_cols) # track definitions bot_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=1) top_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=2) # Placement lv_data_ncol = lv_data_master.num_cols lv_ctl_ncol = lv_ctrl_master.num_cols lv_por_ncol = lv_por_master.num_cols min_sep = self.min_sep_col sup_info = self.get_supply_column_info(xm_layer) inv_ncol = inv_master.num_cols inv_ncol += (inv_ncol & 1) half_ncol = max(lv_data_ncol + min_sep + inv_ncol, inbuf_ncol, core_ncol) # assume 2 inverter chains + margins have smaller width than a single lvl shifter if 2 * buf_ctrl_lv_master.num_cols + min_sep > lv_ctl_ncol: raise ValueError("buffer too large compared to data level shifter's width") if 2 * buf_por_lv_master.num_cols + min_sep > lv_por_ncol: raise ValueError("buffer too large compared to data level shifter's width") # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_io_table = {lay: [] for lay in lay_range} vdd_core_table = {lay: [] for lay in lay_range} vss_table = {lay: [] for lay in lay_range} # instantiate cells and collect pins pin_dict = {'por': [], 'porb': [], 'VDDIO': [], 'VSS': [], 'por_core': [], 'porb_core': [], 'VDD': []} in_pins = ['din_en', 'por_in', 'ck_en', 'unused'] cur_col = 0 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) new_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_por_lv_master, lv_ctrl_master, lv_por_master, bot_vss, top_vss, in_pins[0], in_pins[1], True, True, False, pin_dict) cur_col = new_col cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, False) cur_col += half_ncol self._draw_data_path(cur_col, data_master, lv_data_master, inv_master, bot_vss, True, 'data_', pin_dict) cur_col += min_sep self._draw_data_path(cur_col, clk_master, lv_data_master, inv_master, bot_vss, False, 'clk_', pin_dict) cur_col += half_ncol + self.sub_sep_col // 2 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) cur_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_por_lv_master, lv_ctrl_master, lv_por_master, bot_vss, top_vss, in_pins[2], in_pins[3], True, True, False, pin_dict, flip_lr=True) # add tapes on bottom tile max_col = self._draw_bot_supply_column(tap_info_list, sup_info, vdd_core_table, vss_table, ridx_p, ridx_n) max_col = max(cur_col, max_col) self.set_mos_size(num_cols=max_col) xh = self.bound_box.xh # connect and export supply pins vss_table[hm_layer].extend(pin_dict['VSS']) vdd_core_table[hm_layer].extend(pin_dict['VDD']) vdd_io_table[hm_layer].extend(pin_dict['VDDIO']) vss_list = vdd_io_list = vdd_core_list = [] for lay in range(hm_layer, xm_layer + 1, 2): vss_list = self.connect_wires(vss_table[lay], upper=xh) vdd_core_list = self.connect_wires(vdd_core_table[lay], upper=xh) vdd_io_list = self.connect_wires(vdd_io_table[lay], upper=xh) vss_vm = vss_table[vm_layer] self.add_pin('VDDIO', vdd_io_list) self.add_pin('VDDCore', vdd_core_list) self.add_pin('VSS', vss_list) self.add_pin('VDDIO_vm', vdd_io_table[vm_layer], hide=True) self.add_pin('VDDCore_vm', vdd_core_table[vm_layer], hide=True) self.add_pin('VSS_vm', vss_vm, hide=True) self.add_pin('VDDIO_conn', vdd_io_table[conn_layer], hide=True) self.add_pin('VDDCore_conn', vdd_core_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) # connect VDDCore signals grid = self.grid tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') xm_w = tr_manager.get_width(xm_layer, 'sig') xm_w_hs = tr_manager.get_width(xm_layer, 'sig_hs') # VDD, por_buf, clkn, clkp, data, data_async, porb_buf, data_en, clk_in, por_in idx_list = tr_manager.place_wires(xm_layer, ['sup', 'sig', 'sig_hs', 'sig_hs', 'sig_hs', 'sig_hs', 'sig', 'sig', 'sig', 'sig'], align_track=vdd_core_list[0].track_id.base_index)[1] # POR por_core, porb_core = pin_dict['por_in_buf'] por_list = pin_dict['por_core'] porb_list = pin_dict['porb_core'] por_list.append(por_core) porb_list.append(porb_core) por_core, porb_core = self.connect_differential_tracks(por_list, porb_list, xm_layer, idx_list[1], idx_list[6], width=xm_w) # extend POR signals so they are symmetric with respect to center self.extend_wires([por_core, porb_core], upper=2 * self.bound_box.xm - por_core.lower) # VDDCore data and control signals match_wires = [pin_dict['data_out'], pin_dict['data_async'], pin_dict['clk_outp'], pin_dict['clk_outn']] results = self.connect_matching_tracks(match_wires, xm_layer, [idx_list[4], idx_list[5], idx_list[3], idx_list[2]], width=xm_w_hs, track_lower=0) self.add_pin('odat', results[0], mode=PinMode.LOWER) self.add_pin('odat_async', results[1], mode=PinMode.LOWER) self.add_pin('oclkp', results[2], mode=PinMode.LOWER) self.add_pin('oclkn', results[3], mode=PinMode.LOWER) in_info_list = [('din_en', 'data_en', idx_list[7]), ('por_in', 'por', idx_list[9]), ('unused', 'unused', -1), ('ck_en', 'clk_en', idx_list[8])] for buf_idx, (key, port_name, tidx) in enumerate(in_info_list): hm_warr = pin_dict[key][0] vm_tidx_ref = pin_dict[key + '_buf'][1].track_id.base_index vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx_ref, 'sig', 'sig', up=((buf_idx & 1) == 0)) vm_warr = self.connect_to_tracks(hm_warr, TrackID(vm_layer, vm_tidx, width=vm_w), min_len_mode=MinLenMode.LOWER) if key == 'unused': self.connect_to_tracks(vm_warr, bot_vss) else: self.add_pin(port_name, self.connect_to_tracks(vm_warr, TrackID(xm_layer, tidx, width=xm_w), track_lower=0), mode=PinMode.LOWER) # connect VDDIO signals idx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'sup', 'sig', 'sig'], align_track=vdd_io_list[0].track_id.base_index, align_idx=2)[1] for name, ename in [('din_en', 'data'), ('ck_en', 'clk')]: wp, wn = self.connect_differential_tracks(pin_dict[f'{name}_out'], pin_dict[f'{name}_outb'], xm_layer, idx_list[1], idx_list[3], width=xm_w) self.connect_to_track_wires(wp, pin_dict[f'{ename}_en']) self.connect_to_track_wires(wn, pin_dict[f'{ename}_enb']) rp, rn = self.connect_differential_tracks(pin_dict['por_in_out'], pin_dict['por_in_outb'], xm_layer, idx_list[0], idx_list[4], width=xm_w) self.connect_differential_wires(pin_dict['por'], pin_dict['porb'], rp, rn) idx_list = tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sup'], align_track=vdd_io_table[vm_layer][0].track_id.base_index, align_idx=-1)[1] rp, rn = self.connect_differential_tracks(rp, rn, vm_layer, idx_list[0], idx_list[1], width=tr_manager.get_width(vm_layer, 'sig')) self.add_pin('por_vccl', rp) self.add_pin('porb_vccl', rn) # connect io pad signals in_pins = pin_dict['data_in'] in_pins.append(pin_dict['clk_inp'][0]) clkn = pin_dict['clk_inn'][0] in_tidx = grid.coord_to_track(xm_layer, in_pins[0].middle, mode=RoundMode.GREATER_EQ) ck_tidx = grid.coord_to_track(xm_layer, clkn.middle, mode=RoundMode.LESS_EQ) io_w = tr_manager.get_width(xm_layer, 'padout') in_warr = self.connect_to_tracks(in_pins, TrackID(xm_layer, in_tidx, width=io_w), min_len_mode=MinLenMode.MIDDLE) clk_warr = self.connect_to_tracks(clkn, TrackID(xm_layer, ck_tidx, width=io_w), track_lower=in_warr.lower, track_upper=in_warr.upper) self.add_pin('iopad', in_warr) self.add_pin('iclkn', clk_warr) # setup schematic parameters rm_keys = ['dual_output', 'invert_out'] data_lv_sch_params = lv_data_master.sch_params.copy(remove=rm_keys) ctrl_lv_sch_params = lv_ctrl_master.sch_params.copy(remove=rm_keys) por_lv_sch_params = lv_por_master.sch_params.copy(remove=rm_keys) self.sch_params = dict( data_params=data_master.sch_params, clk_params=clk_master.sch_params, data_lv_params=data_lv_sch_params, ctrl_lv_params=ctrl_lv_sch_params, por_lv_params=por_lv_sch_params, buf_ctrl_lv_params=buf_ctrl_lv_master.sch_params.copy(remove=['dual_output']), buf_por_lv_params=buf_por_lv_master.sch_params.copy(remove=['dual_output']), inv_params=inv_master.sch_params, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) arr_info = self.arr_info tr_manager = self.tr_manager pupd_params: Param = self.params['pupd_params'] unit_params: Param = self.params['unit_params'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 ym_layer = xm_layer + 1 # create masters unit_params = unit_params.copy( append=dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n)) unit_master = self.new_template(OutputDriverCore, params=unit_params) vm_tidx = unit_master.get_port('out').get_pins()[0].track_id.base_index pupd_params = pupd_params.copy(append=dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, sig_locs=dict(out=vm_tidx))) pupd_master = self.new_template(PullUpDown, params=pupd_params) # placement # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} vss_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} sup_info = self.get_supply_column_info(xm_layer) sub_sep = self.sub_sep_col sub_sep2 = sub_sep // 2 # instantiate cells and collect pins pin_dict = {'din': [], 'out': [], 'out_hm': [], 'en': [], 'enb': []} cur_col = 0 din_type = 'sig_hs' tap_list = [] sup_unit_sep = sub_sep2 # first column tap_list.append((cur_col, False)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, False) # make sure we have room for din wire sup_tidx = max(vdd_table[vm_layer][-1].track_id.base_index, vss_table[vm_layer][-1].track_id.base_index) din_tidx = tr_manager.get_next_track(vm_layer, sup_tidx, 'sup', din_type, up=True) en_tidx = tr_manager.get_next_track(vm_layer, din_tidx, din_type, 'sig', up=True) unit_col = arr_info.coord_to_col(self.grid.track_to_coord( vm_layer, en_tidx), round_mode=RoundMode.GREATER_EQ) sup_unit_sep = max(sup_unit_sep, unit_col - cur_col) # make sure unit element is in pitch on ym_layer ncol_core = unit_master.num_cols + sup_info.ncol ncol_unit = 2 * sup_unit_sep + ncol_core if self.top_layer >= ym_layer: ncol_unit = arr_info.round_up_to_block_size(ym_layer, ncol_unit, even_diff=True, half_blk=False) sup_unit_sep = (ncol_unit - ncol_core) // 2 cur_col += sup_unit_sep cur_col = self._draw_unit_cells(cur_col, unit_master, pin_dict, range(4)) cur_col += sup_unit_sep # second column tap_list.append((cur_col, False)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, False) cur_col += sup_unit_sep new_col = self._draw_unit_cells(cur_col, unit_master, pin_dict, range(1, 3)) # pull up/pull down pupd_inst = self.add_tile(pupd_master, 0, cur_col) self._record_pins(pupd_inst, pin_dict, False) cur_col = max(new_col + sup_unit_sep, cur_col + pupd_master.num_cols + sub_sep2) # last supply column tap_list.append((cur_col, True)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, True) self._tap_columns = ImmutableList(tap_list) self.set_mos_size(num_cols=cur_col) # routing # supplies vss = vdd = None for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd = vdd_table[lay] if lay == hm_layer: vss.append(pupd_inst.get_pin('VSS')) vdd.append(pupd_inst.get_pin('VDD')) vdd = self.connect_wires(vdd) vss = self.connect_wires(vss) self.add_pin(f'VDD_{lay}', vdd, hide=True) self.add_pin(f'VSS_{lay}', vss, hide=True) vdd = vdd[0] vss = vss[0] self.add_pin('VDD', vdd) self.add_pin('VSS', vss) self.add_pin('VDD_vm', self.connect_wires(vdd_table[vm_layer]), hide=True) self.add_pin('VSS_vm', vss_table[vm_layer], hide=True) self.add_pin('VDD_conn', vdd_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) # signals self.connect_wires(pin_dict['out_hm']) din = self.connect_wires(pin_dict['din']) out_list = pin_dict['out'] out_upper = max((warr.upper for warr in out_list)) out = self.connect_wires(out_list, upper=out_upper)[0] en_list = pin_dict['en'] enb_list = pin_dict['enb'] p_en_1 = self.connect_wires(en_list[4:])[0] n_enb_1 = self.connect_wires(enb_list[4:])[0] din = self.connect_to_tracks( din, TrackID(vm_layer, din_tidx, width=tr_manager.get_width(vm_layer, din_type))) pub = self.connect_to_tracks(pupd_inst.get_pin('puenb'), p_en_1.track_id, min_len_mode=MinLenMode.LOWER) pd = self.connect_to_tracks(pupd_inst.get_pin('pden'), n_enb_1.track_id, min_len_mode=MinLenMode.UPPER) self.add_pin('din', din) self.add_pin('p_en_drv<0>', en_list[0]) self.add_pin('n_enb_drv<0>', enb_list[0]) self.add_pin('tristateb', self.connect_wires(en_list[1:4])) self.add_pin('tristate', self.connect_wires(enb_list[1:4])) self.add_pin('p_en_drv<1>', p_en_1) self.add_pin('n_enb_drv<1>', n_enb_1) self.add_pin('weak_puenb', pub) self.add_pin('weak_pden', pd) self.add_pin('txpadout', out) self.sch_params = dict( unit_params=unit_master.sch_params, pupd_params=pupd_master.sch_params, )
def _draw_row(self, unit_master: PhaseInterpUnit, inv_master: InvCore, tile_idx: int, nbits: int, flip_en: bool, draw_sub: bool, prefix: str, vss_tile: int, vdd_tile: int, abut_tristates: bool) -> Dict[str, Union[List[WireArray], WireArray]]: vm_layer = self.conn_layer + 2 vm_sup_w = self.tr_manager.get_width(vm_layer, 'sup') vm_sup_l = self.grid.get_next_length(vm_layer, vm_sup_w, 0, even=True) if abut_tristates: unit_sep = 0 col_margin = 0 else: col_margin = unit_master.col_margin min_sep = self.min_sep_col min_sep += (min_sep & 1) unit_sep = max(min_sep - 2 * col_margin, 0) unit_sep += (unit_sep & 1) # gather wires, connect/export enable signals pin_list = ['pout', 'nout', 'nin', 'VDD', 'VSS', 'VDD_vm', 'VSS_vm'] pin_dict = {name: [] for name in pin_list} cur_col = 1 if draw_sub else 0 unit_ncol = unit_master.num_cols for idx in range(nbits): inst = self.add_tile(unit_master, tile_idx, cur_col) if abut_tristates: en = inst.get_pin('en') enb = inst.get_pin('enb') en_tr_idx = self.grid.coord_to_track(vm_layer, en.middle, RoundMode.LESS) enb_tr_idx = self.grid.coord_to_track(vm_layer, en.middle, RoundMode.GREATER) sup_tr_idx = self.tr_manager.get_next_track(vm_layer, en_tr_idx, 'sig', 'sup', up=False) if flip_en: en, enb = self.connect_differential_tracks(en, enb, vm_layer, en_tr_idx, enb_tr_idx) else: en, enb = self.connect_differential_tracks(en, enb, vm_layer, enb_tr_idx, en_tr_idx) sup_w = self.add_wires(vm_layer, sup_tr_idx, en.middle - vm_sup_l // 2, en.middle + vm_sup_l // 2, width=vm_sup_w) if idx & 1: pin_dict['VDD_vm'].append(sup_w) else: pin_dict['VSS_vm'].append(sup_w) for pin in pin_list: if pin not in ['VDD_vm', 'VSS_vm']: pin_dict[pin].append(inst.get_pin(pin)) else: for name in pin_list: pin_dict[name].append(inst.get_pin(name)) enl = inst.get_pin('enl') enr = inst.get_pin('enr') en = inst.get_pin('en') enb = inst.get_pin('enb') if flip_en: en, enb = self.connect_differential_wires(en, enb, enr, enl) else: en, enb = self.connect_differential_wires(en, enb, enl, enr) # NOTE: LSB closest to the output to help with nonlinearity bit_idx = nbits - 1 - idx self.add_pin(f'{prefix}_en<{bit_idx}>', en) self.add_pin(f'{prefix}_enb<{bit_idx}>', enb) cur_col += unit_ncol + unit_sep # short hm_layer wires pout = self.connect_wires(pin_dict['pout'])[0] nout = self.connect_wires(pin_dict['nout'])[0] in_warr = self.connect_wires(pin_dict['nin'])[0] self.add_pin(prefix + '_in', in_warr) # connect output inverter cur_col += self.min_sep_col vm_w = self.tr_manager.get_width(vm_layer, 'sig_hs') inst = self.add_tile(inv_master, tile_idx, cur_col) buf_out = inst.get_pin('out') vm_tidx = self.tr_manager.get_next_track(vm_layer, buf_out.track_id.base_index, 'sig', 'sig_hs', up=False) inv_in = self.connect_to_tracks([pout, nout, inst.get_pin('in')], TrackID(vm_layer, vm_tidx, width=vm_w)) pin_dict['out'] = [inv_in] pin_dict['VDD'].extend(inst.port_pins_iter('VDD')) pin_dict['VSS'].extend(inst.port_pins_iter('VSS')) # connect supplies vss_tid = self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=vss_tile) vdd_tid = self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=vdd_tile) vss = self.connect_to_tracks(pin_dict['VSS'], vss_tid) vdd = self.connect_to_tracks(pin_dict['VDD'], vdd_tid) vss_vm_list = [] vdd_vm_list = [] for vss_vm, vdd_vm in zip(pin_dict['VSS_vm'], pin_dict['VDD_vm']): vss_vm, vdd_vm = self.connect_differential_wires(vss, vdd, vss_vm, vdd_vm) vss_vm_list.append(vss_vm) vdd_vm_list.append(vdd_vm) return {'mid': pin_dict['out'], 'out': buf_out, 'VSS': vss_vm_list, 'VDD': vdd_vm_list, 'VSS_hm': vss, 'VDD_hm': [vdd]}
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) seg_p: int = self.params['seg_p'] seg_n: int = self.params['seg_n'] w_p: int = self.params['w_p'] w_n: int = self.params['w_n'] stack: int = self.params['stack'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 #if (seg_p - seg_n) % 2 != 0: # raise ValueError('seg_p - seg_n should be even') if w_p == 0: w_p = self.place_info.get_row_place_info(ridx_p).row_info.width if w_n == 0: w_n = self.place_info.get_row_place_info(ridx_n).row_info.width pin_tidx = sig_locs.get( 'pin', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)) nin_tidx = sig_locs.get( 'nin', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)) nout_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='padout') pout_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='padout') fg_p = seg_p * stack fg_n = seg_n * stack num_col = max(fg_p, fg_n) p_col = (num_col - fg_p) // 2 n_col = (num_col - fg_n) // 2 pmos = self.add_mos(ridx_p, p_col, seg_p, w=w_p, stack=stack) nmos = self.add_mos(ridx_n, n_col, seg_n, w=w_n, stack=stack) self.set_mos_size(num_cols=num_col) # connect supplies vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='sup') vss_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='sup') vdd = self.connect_to_tracks(pmos.s, vdd_tid) vss = self.connect_to_tracks(nmos.s, vss_tid) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # connect inputs hm_w = self.tr_manager.get_width(hm_layer, 'sig') pden = self.connect_to_tracks(nmos.g, TrackID(hm_layer, nin_tidx, width=hm_w), min_len_mode=MinLenMode.MIDDLE) puenb = self.connect_to_tracks(pmos.g, TrackID(hm_layer, pin_tidx, width=hm_w), min_len_mode=MinLenMode.MIDDLE) self.add_pin('pden', pden) self.add_pin('puenb', puenb) # connect output vm_w_pad = self.tr_manager.get_width(vm_layer, 'padout') nout = self.connect_to_tracks(nmos.d, nout_tid, min_len_mode=MinLenMode.MIDDLE) pout = self.connect_to_tracks(pmos.d, pout_tid, min_len_mode=MinLenMode.MIDDLE) vm_tidx = sig_locs.get( 'out', self.grid.coord_to_track(vm_layer, nout.middle, mode=RoundMode.NEAREST)) out = self.connect_to_tracks([nout, pout], TrackID(vm_layer, vm_tidx, width=vm_w_pad)) self.add_pin('out', out) self.add_pin('nout', nout, hide=True) self.add_pin('pout', pout, hide=True) # compute schematic parameters. th_p = self.place_info.get_row_place_info(ridx_p).row_info.threshold th_n = self.place_info.get_row_place_info(ridx_n).row_info.threshold self.sch_params = dict( seg_p=seg_p, seg_n=seg_n, lch=self.place_info.lch, w_p=w_p, w_n=w_n, th_p=th_p, th_n=th_n, stack_p=stack, stack_n=stack, )
def draw_layout(self): # type: () -> None route_layer = self.params['layer'] bias_config = self.params['bias_config'] nwire_real = self.params['nwire'] width = self.params['width'] space_sig = self.params['space_sig'] top = self.params['top'] is_end = self.params['is_end'] grid = self.grid res = grid.resolution bot_layer = route_layer - 1 top_layer = route_layer + 1 route_dir = grid.get_direction(route_layer) is_horiz = (route_dir == 'x') # compute size tot_w, tot_h = self.get_block_size(grid, route_layer, bias_config, nwire_real, width=width, space_sig=space_sig) dim_par = tot_w if is_horiz else tot_h bbox = BBox(0, 0, tot_w, tot_h, res, unit_mode=True) self.prim_top_layer = top_layer self.prim_bound_box = bbox self.array_box = bbox self.add_cell_boundary(bbox) # compute wire location self._route_tids = locs = self.get_route_tids(self.grid, route_layer, 0, bias_config, nwire_real, width=width, space_sig=space_sig) pitch = locs[nwire_real + 1][0] - locs[0][0] tr_upper = bbox.width_unit if is_horiz else bbox.height_unit sh_warr = self.add_wires(route_layer, locs[0][0], 0, tr_upper, width=1, num=2, pitch=pitch, unit_mode=True) sup_layer = top_layer if top else bot_layer pitch2 = grid.get_track_pitch(sup_layer, unit_mode=True) // 2 sup_np2, sup_w, sup_spe2, sup_p2 = bias_config[sup_layer] sup_unit = sup_np2 * pitch2 num = dim_par // sup_unit if num == 1: num = 1 if sup_p2 == 0 else 1 + (sup_np2 - 2 * sup_spe2) // sup_p2 sup_tid = TrackID(sup_layer, (sup_spe2 - 1) / 2, width=sup_w, num=num, pitch=sup_p2 / 2) else: if sup_p2 == 0: sup_tid = TrackID(sup_layer, (sup_spe2 - 1) / 2, width=sup_w, num=num, pitch=sup_np2 / 2) else: # TODO: come back to fix this raise ValueError('umm...see Eric') if is_end: sup_n = sup_tid.num if sup_n > 1: sup_pitch = sup_tid.pitch new_tid = TrackID(sup_layer, sup_tid.base_index, width=sup_w, num=sup_n - 1, pitch=sup_pitch) warr = self.connect_to_tracks(sh_warr, new_tid) self.add_pin('sup', warr, show=False) sup_tid = TrackID(sup_layer, sup_tid.base_index + (sup_n - 1) * sup_pitch, width=sup_w) shl = grid.get_wire_bounds(route_layer, locs[0][0], width=1, unit_mode=True)[0] shr = grid.get_wire_bounds(route_layer, locs[nwire_real + 1][0], width=1, unit_mode=True)[1] sh_singles = sh_warr.to_warr_list() if top: test_warr = self.connect_to_tracks(sh_singles[0], sup_tid) vext = shl - test_warr.lower_unit else: test_warr = self.connect_to_tracks(sh_singles[1], sup_tid) vext = test_warr.upper_unit - shr warr = self.add_wires(sup_layer, sup_tid.base_index, shl - vext, shr + vext, width=sup_w, unit_mode=True) else: warr = self.connect_to_tracks(sh_warr, sup_tid) self.add_pin('shield', sh_warr, show=False) self.add_pin('sup', warr, show=False) sup_box = warr.get_bbox_array(self.grid).get_overall_bbox() self._sup_intv = sup_box.get_interval(route_dir, unit_mode=True)
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) mux_params: Param = self.params['mux_params'] flop_params: Param = self.params['flop_params'] inv_params: Param = self.params['inv_params'] vm_pitch: HalfInt = HalfInt.convert(self.params['vm_pitch']) vm_pitch_htr = vm_pitch.dbl_value # create masters mux_params = mux_params.copy( append=dict(pinfo=pinfo, vertical_sel=False)) flop_params = flop_params.copy(append=dict( pinfo=pinfo, has_rstlb=True, swap_outbuf=True, out_pitch=vm_pitch)) mux_master: Mux2to1Matched = self.new_template(Mux2to1Matched, params=mux_params) flop_master: FlopStrongArm = self.new_template(FlopStrongArm, params=flop_params) inv_in_tidx = mux_master.get_port( 'in<0>').get_pins()[0].track_id.base_index inv_params = inv_params.copy( append=dict(pinfo=pinfo, sig_locs={'in': inv_in_tidx})) inv_master: InvCore = self.new_template(InvCore, params=inv_params) # placement min_sep = self.min_sep_col min_sep += (min_sep & 1) buf_ncol = inv_master.num_cols mux_ncol = mux_master.num_cols flop_ncol = flop_master.num_cols ncol_tot = max(flop_ncol, mux_ncol + 2 * min_sep + 2 * buf_ncol) mux_off = (ncol_tot - mux_ncol) // 2 flop_off = (ncol_tot - flop_ncol) // 2 mux_in = self.add_tile(mux_master, 0, mux_off) flop = self.add_tile(flop_master, 1, flop_off) mux_out = self.add_tile(mux_master, 3, mux_off) invl = self.add_tile(inv_master, 3, 0) invr = self.add_tile(inv_master, 3, ncol_tot, flip_lr=True) self.set_mos_size() yh = self.bound_box.yh xm = self.bound_box.xm # routing vm_layer = self.conn_layer + 2 grid = self.grid tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_ck = tr_manager.get_width(vm_layer, 'clk') # supplies vdd_list = [] vss_list = [] for inst in [mux_in, flop, mux_out, invl, invr]: vdd_list.extend(inst.port_pins_iter('VDD')) vss_list.extend(inst.port_pins_iter('VSS')) self.add_pin('VDD', self.connect_wires(vdd_list), connect=True) self.add_pin('VSS', self.connect_wires(vss_list), connect=True) # input mux flop_clk = mux_in.get_pin('out') self.connect_to_track_wires(flop_clk, flop.get_pin('clk')) in0 = mux_in.get_pin('in<0>') in1 = mux_in.get_pin('in<1>') clk_vm_tidx = flop_clk.track_id.base_index in_vm_test = tr_manager.get_next_track(vm_layer, clk_vm_tidx, 'sig', 'clk', up=True) in_dhtr = (-(in_vm_test - clk_vm_tidx).dbl_value // vm_pitch_htr) * vm_pitch_htr in_delta = HalfInt(in_dhtr) in0 = self.connect_to_tracks(in0, TrackID(vm_layer, clk_vm_tidx + in_delta, width=vm_w_ck), min_len_mode=MinLenMode.LOWER) in1 = self.connect_to_tracks(in1, TrackID(vm_layer, clk_vm_tidx - in_delta, width=vm_w_ck), min_len_mode=MinLenMode.LOWER) self.add_pin('launch', in0) self.add_pin('measure', in1) # divider flop clkp = flop.get_pin('outp') clkn = flop.get_pin('outn') self.connect_differential_wires(mux_in.get_pin('nsel'), mux_in.get_pin('nselb'), clkp, clkn) self.connect_to_track_wires(clkp, mux_in.get_pin('psel')) self.connect_to_track_wires(clkn, mux_in.get_pin('pselb')) self.connect_to_track_wires(flop.get_pin('inn'), clkp) self.connect_to_track_wires(flop.get_pin('inp'), clkn) self.connect_to_tracks(mux_out.get_pin('in<0>'), clkp.track_id, track_upper=yh, track_lower=clkp.lower) self.reexport(flop.get_port('rstlb')) self.reexport(flop.get_port('rstlb_vm_l')) self.reexport(flop.get_port('rsthb')) # output mux clkn_tid = clkn.track_id vm_sp_le = grid.get_line_end_space(vm_layer, clkn_tid.width) clk_dcd = self.connect_to_tracks(mux_out.get_pin('in<1>'), clkn_tid, track_lower=clkn.upper + vm_sp_le, track_upper=yh) clk_out = self.extend_wires(mux_out.get_pin('out'), upper=yh) self.add_pin('clk_dcd', clk_dcd) self.add_pin('clk_out', clk_out) # output mux select signals out_sel = invl.get_pin('out') out_selb = invr.get_pin('out') dcc_byp_delta = tr_manager.get_next_track( vm_layer, out_selb.track_id.base_index, 'sig', 'sig', up=True) - clk_vm_tidx dcc_byp_dhtr = -(-dcc_byp_delta.dbl_value // vm_pitch_htr) * vm_pitch_htr dcc_byp_delta = HalfInt(dcc_byp_dhtr) dcc_byp_tidx = clk_vm_tidx + dcc_byp_delta out_selb_vm_tidx = clk_vm_tidx - dcc_byp_delta self.connect_to_track_wires(mux_out.get_pin('psel'), out_sel) self.connect_to_track_wires(mux_out.get_pin('pselb'), out_selb) nsel, nselb = self.connect_differential_wires(out_sel, out_selb, mux_out.get_pin('nsel'), mux_out.get_pin('nselb')) sel_hm_list = [] selb_vm = self.connect_to_tracks([nselb, invl.get_pin('in')], TrackID(vm_layer, out_selb_vm_tidx, width=vm_w), track_upper=yh, ret_wire_list=sel_hm_list) # get differential matching xl = min((wire.lower for wire in sel_hm_list)) xh = 2 * xm - xl self.extend_wires([nsel, nselb], lower=xl, upper=xh) dcc_byp = self.connect_to_tracks(invr.get_pin('in'), TrackID(vm_layer, dcc_byp_tidx, width=vm_w), track_lower=selb_vm.lower, track_upper=yh) self.add_pin('dcc_byp', dcc_byp) self.sch_params = dict( mux_params=mux_master.sch_params, flop_params=flop_master.sch_params, inv_params=inv_master.sch_params, )
def connect_bias_shields( cls, # type: BiasShield template, # type: TemplateBase layer, # type: int bias_config, # type: Dict[int, Tuple[int, ...]] warr_list2, # type: List[Union[WireArray, Iterable[WireArray]]] offset, # type: int width=1, # type: int space_sig=0, # type: int tr_lower=None, # type: Optional[int] tr_upper=None, # type: Optional[int] lu_end_mode=0, # type: int sup_warrs=None, # type: Optional[Union[WireArray, List[WireArray]]] add_end=True, # type: bool extend_tracks=True, # type: bool ): # type: (...) -> BiasInfo grid = template.grid nwire = len(warr_list2) blk_w, blk_h = cls.get_block_size(grid, layer, bias_config, nwire, width=width, space_sig=space_sig) route_tids = cls.get_route_tids(grid, layer, offset, bias_config, nwire, width=width, space_sig=space_sig) tr_dir = grid.get_direction(layer) is_horiz = tr_dir == 'x' qdim = blk_w if is_horiz else blk_h if lu_end_mode == 0: min_len_mode = 0 elif lu_end_mode & 2 != 0: min_len_mode = 1 else: min_len_mode = -1 tr_warr_list = [] if tr_lower is not None: tr_offset = tr_lower % qdim elif tr_upper is not None: tr_offset = tr_upper % qdim else: tr_offset = 0 for warr_list, (tidx, tr_width) in zip(warr_list2, islice(route_tids, 1, nwire + 1)): if isinstance(warr_list, WireArray): warr_list = [warr_list] cur_tid = TrackID(layer, tidx, width=width) tr_warr = template.connect_to_tracks(warr_list, cur_tid, min_len_mode=min_len_mode) tr_warr_list.append(tr_warr) if tr_warr is not None: if tr_lower is None: tr_lower = tr_warr.lower_unit else: tr_lower = min(tr_lower, tr_warr.lower_unit) if tr_upper is None: tr_upper = tr_warr.upper_unit else: tr_upper = max(tr_upper, tr_warr.upper_unit) if tr_lower is None or tr_upper is None: raise ValueError('Cannot determine bias shield location.') # round lower/upper to nearest coordinates, then draw shields. nstart = (tr_lower - tr_offset) // qdim nstop = -(-(tr_upper - tr_offset) // qdim) tr_lower = nstart * qdim + tr_offset tr_upper = nstop * qdim + tr_offset tmp = cls.draw_bias_shields(template, layer, bias_config, nwire, offset, tr_lower, tr_upper, width=width, space_sig=space_sig, sup_warrs=sup_warrs) sup_warrs, shields = tmp # get bias information, extend tracks, and draw ends if is_horiz: p0 = (tr_lower, offset) p1 = (tr_upper, offset + blk_h) else: p0 = (offset, tr_lower) p1 = (offset + blk_w, tr_upper) if lu_end_mode == 0: if extend_tracks: tr_warr_list = template.extend_wires(tr_warr_list, lower=tr_lower, upper=tr_upper, unit_mode=True) else: if lu_end_mode & 2 != 0: if extend_tracks: tr_warr_list = template.extend_wires(tr_warr_list, upper=tr_upper, unit_mode=True) eorient = 'MY' if is_horiz else 'MX' loc = p0 else: if extend_tracks: tr_warr_list = template.extend_wires(tr_warr_list, lower=tr_lower, unit_mode=True) eorient = 'R0' loc = (tr_upper, offset) if is_horiz else (offset, tr_upper) if add_end: params = dict( layer=layer, nwire=nwire, bias_config=bias_config, width=width, space_sig=space_sig, ) end_master = template.new_template(params=params, temp_cls=BiasShieldEnd) inst = template.add_instance(end_master, loc=loc, orient=eorient, unit_mode=True) sup_warrs.extend(inst.get_all_port_pins('sup', layer=layer + 1)) return BiasInfo(tracks=tr_warr_list, supplies=sup_warrs, p0=p0, p1=p1, shields=shields)
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) pi_params: Param = self.params['pi_params'] dc_params: Param = self.params['dc_params'] inv_params: Param = self.params['inv_params'] nand_params: Param = self.params['nand_params'] ncores: int = self.params['num_core'] nbits: int = self.params['nbits'] split_nand: bool = self.params['split_nand'] split_inv: bool = self.params['split_inv'] pi_tile: int = self.params['pi_tile'] dc_tile: int = self.params['dc_tile'] inv_tile_l = dc_tile + 1 inv_tile_h = dc_tile + 3 hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 grid = self.grid tr_manager = self.tr_manager # Recompute sizes for the inverter and nand inv_seg: int = inv_params['seg'] nand_seg: int = nand_params['seg'] split_inv = split_inv and (inv_seg > 1) split_nand = split_nand and (nand_seg > 1) if split_inv: inv_seg_2 = inv_seg // 2 inv_seg_1 = inv_seg - inv_seg_2 else: inv_seg_1 = inv_seg inv_seg_2 = None if split_nand: nand_seg_2 = nand_seg // 2 nand_seg_1 = nand_seg - nand_seg_2 else: nand_seg_1 = nand_seg nand_seg_2 = None # create masters low_pinfo = self.get_tile_pinfo(inv_tile_l) high_pinfo = self.get_tile_pinfo(inv_tile_h) pi_params = pi_params.copy( append=dict(pinfo=pinfo, nbits=nbits + 1, flip_b_en=True)) dc_params = dc_params.copy(append=dict(pinfo=pinfo, substrate_row=True, vertical_out=True, draw_substrate_row=False, tile0=3, tile1=1, flip_vm=True)) pi_master: PhaseInterpolator = self.new_template(PhaseInterpolator, params=pi_params) dc_master: DelayCellCore = self.new_template(DelayCellCore, params=dc_params) dc0_params = dc_params.copy(append=dict(feedback=True)) dc0_master: DelayCellCore = self.new_template(DelayCellCore, params=dc0_params) # Place all the cells ntr, vert_trs = tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sup']) ncol = self.arr_info.get_column_span(vm_layer, ntr) pi = self.add_tile(pi_master, pi_tile, ncol) dc_arr = [self.add_tile(dc0_master, dc_tile, 0)] sep = max(self.min_sep_col, self.get_hm_sp_le_sep_col()) curr_col = dc_master.num_cols + sep for _ in range(ncores - 1): dc_arr.append(self.add_tile(dc_master, dc_tile, curr_col)) curr_col += dc_master.num_cols + sep nand_params_l = nand_params.copy(append=dict(pinfo=low_pinfo, vertical_sup=True, vertical_out=False, seg=nand_seg_1)) nand_master_l: NAND2Core = self.new_template(NAND2Core, params=nand_params_l) nand_l = self.add_tile(nand_master_l, inv_tile_l, curr_col) if split_nand: nand_params_h = nand_params.copy(append=dict(pinfo=high_pinfo, vertical_sup=True, vertical_out=False, seg=nand_seg_2)) nand_master_h: NAND2Core = self.new_template(NAND2Core, params=nand_params_h) inv_in_tid_h = nand_master_h.get_track_index(1, MOSWireType.G, wire_name='sig', wire_idx=0) nand_h = self.add_tile(nand_master_h, inv_tile_h, curr_col) nand_sch_params = [ nand_master_l.sch_params, nand_master_h.sch_params ] else: inv_in_tid_h = None nand_h = None nand_sch_params = [nand_master_l.sch_params] inv_col = curr_col + nand_master_l.num_cols + sep inv_in_tid = nand_master_l.get_track_index(1, MOSWireType.G, wire_name='sig', wire_idx=0) inv_params_l = inv_params.copy(append=dict(pinfo=low_pinfo, vertical_sup=True, seg=inv_seg_1, vertical_out=False, sig_locs=dict( nin=inv_in_tid))) inv_master_l: InvCore = self.new_template(InvCore, params=inv_params_l) inv_l = self.add_tile(inv_master_l, inv_tile_l, inv_col) if split_inv: if inv_in_tid_h: inv_params_h = inv_params.copy( append=dict(pinfo=high_pinfo, vertical_sup=True, seg=inv_seg_2, vertical_out=False, sig_locs=dict(nin=inv_in_tid_h))) else: inv_params_h = inv_params.copy(append=dict(pinfo=high_pinfo, vertical_sup=True, seg=inv_seg_2, vertical_out=False)) inv_master_h: InvCore = self.new_template(InvCore, params=inv_params_h) inv_h = self.add_tile(inv_master_h, inv_tile_h, inv_col) inv_sch_params = [inv_master_l.sch_params, inv_master_h.sch_params] else: inv_h = None inv_sch_params = [inv_master_l.sch_params] # substrate taps vss_hm = [ pi.get_pin('VSS0'), pi.get_pin('VSS1'), self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=8) ] vdd_hm = [ pi.get_pin('VDD_hm'), self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=6) ] ncol_tot = self.num_cols sub = self.add_substrate_contact(0, 0, tile_idx=0, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[0]) sub = self.add_substrate_contact(0, 0, tile_idx=2, seg=ncol_tot) self.connect_to_track_wires(sub, vdd_hm[0]) sub = self.add_substrate_contact(0, 0, tile_idx=4, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[1]) sub = self.add_substrate_contact(0, 0, tile_idx=6, seg=ncol_tot) sub_vdd1 = self.connect_to_tracks(sub, vdd_hm[1]) vdd_hm[1] = sub_vdd1 sub = self.add_substrate_contact(0, 0, tile_idx=8, seg=ncol_tot) sub_vss2 = self.connect_to_tracks(sub, vss_hm[2]) vss_hm[2] = sub_vss2 self.set_mos_size() xh = self.bound_box.xh # routing xm_w = tr_manager.get_width(xm_layer, 'sig') xm_w_sup = tr_manager.get_width(xm_layer, 'sup') xm_w_hs = tr_manager.get_width(xm_layer, 'sig_hs') vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_sup = tr_manager.get_width(vm_layer, 'sup') vm_sep_sup = tr_manager.get_sep(vm_layer, ('sup', 'sup')) xm_vss_tids, xm_vdd_tids = [], [] for tid_arr, sup_hm_arr in [(xm_vss_tids, vss_hm), (xm_vdd_tids, vdd_hm)]: for sup_w in sup_hm_arr: y = grid.track_to_coord(hm_layer, sup_w.track_id.base_index) tid_arr.append( TrackID(xm_layer, grid.coord_to_track(xm_layer, y, mode=RoundMode.NEAREST), width=xm_w_sup)) tr_list = ['sup'] + ['sig'] * (nbits + 1) pidx_list_l = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[0].base_index, align_idx=0)[1] pidx_list_h = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[1].base_index, align_idx=0)[1] tr_list.reverse() nidx_list_l = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[1].base_index, align_idx=-1)[1] nidx_list_h = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[2].base_index, align_idx=-1)[1] io_idx_list = tr_manager.place_wires( xm_layer, ['sig_hs', 'sup', 'sig_hs'], align_track=xm_vdd_tids[0].base_index, align_idx=1)[1] int_idx_list = tr_manager.place_wires( xm_layer, ['sig_hs', 'sup', 'sig_hs'], align_track=xm_vdd_tids[1].base_index, align_idx=1)[1] # PI signals vss_list = pi.get_all_port_pins('VSS') vdd_list = pi.get_all_port_pins('VDD') suf = f'<{nbits}>' en_vss = self.connect_to_track_wires(vss_hm[0:2], pi.get_pin('a_enb' + suf)) en_l = en_vss.lower en_u = en_vss.upper self.connect_to_tracks(vdd_hm[0], pi.get_pin('a_en' + suf).track_id, track_lower=en_l, track_upper=en_u) en_xm_upper = None for idx in range(nbits): suf = f'<{idx}>' enb = self.connect_wires( [pi.get_pin('a_en' + suf), pi.get_pin('b_enb' + suf)], lower=en_l, upper=en_u) en = self.connect_wires( [pi.get_pin('a_enb' + suf), pi.get_pin('b_en' + suf)], lower=en_l, upper=en_u) ptid = TrackID(xm_layer, pidx_list_l[1 + idx], width=xm_w) ntid = TrackID(xm_layer, nidx_list_l[-idx - 2], width=xm_w) if idx == 0: en = self.connect_to_tracks(en, ptid, track_lower=0) en_xm_upper = en.upper else: en = self.connect_to_tracks(en, ptid, track_lower=0, track_upper=en_xm_upper) enb = self.connect_to_tracks(enb, ntid, track_lower=0, track_upper=en_xm_upper) self.add_pin('sp' + suf, en) self.add_pin('sn' + suf, enb) intout = self.connect_to_tracks(pi.get_pin('out'), TrackID(xm_layer, io_idx_list[2], width=xm_w_hs), track_lower=0) self.add_pin('intout', intout) vdd0 = self.connect_to_tracks(vdd_list, xm_vdd_tids[0], track_lower=0, track_upper=xh) vss0 = self.connect_to_tracks(vss_list, xm_vss_tids[0], track_lower=0, track_upper=xh) vss1 = self.connect_to_tracks(vss_list, xm_vss_tids[1], track_lower=0, track_upper=xh) # delay cell signals vdd_list, vss0_list, vss1_list = [], [], [] for dc in dc_arr: vdd_list += dc.get_all_port_pins('VDD') vss0_list += dc.get_all_port_pins('VSS1') vss1_list += dc.get_all_port_pins('VSS0') vdd_list.extend(nand_l.get_all_port_pins('VDD')) vss0_list.extend(nand_l.get_all_port_pins('VSS')) if split_nand: vdd_list.extend(nand_h.get_all_port_pins('VDD')) vss1_list.extend(nand_h.get_all_port_pins('VSS')) vss0_list.extend(inv_l.get_all_port_pins('VSS')) vdd_list.extend(inv_l.get_all_port_pins('VDD')) if split_inv: vdd_list.extend(inv_h.get_all_port_pins('VDD')) vss1_list.extend(inv_h.get_all_port_pins('VSS')) vdd_list.extend([dc.get_pin('bk1_vm') for dc in dc_arr]) self.connect_to_track_wires(vdd_list, vdd_hm[1]) self.connect_to_track_wires(vss0_list, vss_hm[1]) self.connect_to_track_wires(vss1_list, vss_hm[2]) if ncores > 1: for idx in range(ncores - 1, 0, -1): cur_cop = dc_arr[idx].get_pin('co_p') next_in = dc_arr[idx - 1].get_pin('in_p') ptid = TrackID(xm_layer, pidx_list_h[1 + (idx % 2)], width=xm_w) self.connect_to_tracks([cur_cop, next_in], ptid) cur_ci = dc_arr[idx].get_pin('ci_p') next_out = dc_arr[idx - 1].get_pin('out_p') ntid = TrackID(xm_layer, nidx_list_h[-2 - (idx % 2)], width=xm_w) self.connect_to_tracks([cur_ci, next_out], ntid) # NAND to delay cell and PI nand_out_vm_tr = self.grid.coord_to_track( vm_layer, nand_l.get_pin('pout').middle, RoundMode.NEAREST) nand_out_pins = [nand_l.get_pin('pout'), nand_l.get_pin('nout')] if split_nand: nand_out_pins.extend( [nand_h.get_pin('pout'), nand_h.get_pin('nout')]) nand_out_vm_w = self.connect_to_tracks( nand_out_pins, TrackID(vm_layer, nand_out_vm_tr, vm_w)) a_in_buf_xm = self.connect_to_tracks( [nand_out_vm_w, dc_arr[-1].get_pin('in_p')], TrackID(xm_layer, int_idx_list[0], xm_w)) a_in_buf_vm = self.connect_to_tracks( [a_in_buf_xm, pi.get_pin('a_in')], TrackID(vm_layer, vert_trs[0], vm_w)) # Delay Cell to PI b_in_xm = self.connect_to_tracks( dc_arr[-1].get_pin("out_p"), TrackID(xm_layer, int_idx_list[-1], xm_w)) b_in_vm = self.connect_to_tracks([b_in_xm, pi.get_pin("b_in")], TrackID(vm_layer, vert_trs[1], vm_w)) nand_in_hm = [nand_l.get_pin("nin<0>"), vdd_hm[1]] if split_nand: nand_in_hm.append(nand_h.get_pin("nin<0>")) nand_in_vm_tr = tr_manager.get_next_track_obj(nand_out_vm_w.track_id, 'sig', 'sig') self.connect_to_tracks(nand_in_hm, nand_in_vm_tr) # Inv out to nand in inv_out_tr = grid.coord_to_track(vm_layer, inv_l.get_pin("pout").middle, RoundMode.NEAREST) inv_out_hm_w = [ inv_l.get_pin("pout"), inv_l.get_pin("nout"), nand_l.get_pin("nin<1>") ] inv_in_tr = tr_manager.get_next_track(vm_layer, inv_out_tr, 'sig', 'sig') inv_in_hm_w = [inv_l.get_pin('in')] if split_inv: inv_out_hm_w.extend([inv_h.get_pin("nout"), inv_h.get_pin("pout")]) inv_in_hm_w.append(inv_h.get_pin("in")) if split_nand: inv_out_hm_w.append(nand_h.get_pin("nin<1>")) inv_out_w = self.connect_to_tracks(inv_out_hm_w, TrackID(vm_layer, inv_out_tr, vm_w)) a_in = self.connect_to_tracks(inv_in_hm_w, TrackID(vm_layer, inv_in_tr, vm_w)) self.add_pin('a_in', a_in) # Supply routing over nand, dc, and inv vm_wires = [nand_out_vm_w, inv_out_w] for dc in dc_arr: for port_name in dc.port_names_iter(): if port_name not in ['VDD', 'VSS0', 'VSS1']: vm_wires.extend(dc.get_all_port_pins(port_name, vm_layer)) # for idx, wire in enumerate(vm_wires): # self.add_pin(f'vm_wire_{idx}', wire) vss_vm_arr = pi.get_all_port_pins('VSS')[0] vdd_vm_arr = pi.get_all_port_pins('VDD')[0] vss_vm, vdd_vm = [], [] for wire_arr, target_wire, vm_list in [(vss_vm_arr, vss_hm[2], vss_vm), (vdd_vm_arr, vdd_hm[1], vdd_vm) ]: for wire in wire_arr.warr_iter(): tr_idx = wire.track_id.base_index min_idx = tr_manager.get_next_track(vm_layer, tr_idx, 'sup', 'sig', up=False) max_idx = tr_manager.get_next_track(vm_layer, tr_idx, 'sup', 'sig', up=True) will_intersect = False for sig_wire in vm_wires: if min_idx < sig_wire.track_id.base_index <= max_idx: will_intersect = True if not will_intersect: self.connect_to_tracks(wire, target_wire.track_id) vm_list.append(wire) vdd1 = self.connect_to_tracks(vdd_vm, xm_vdd_tids[1], track_lower=0, track_upper=xh) vss2 = self.connect_to_tracks(vss_vm, xm_vss_tids[2], track_lower=0, track_upper=xh) self.add_pin('VDD', [vdd0, vdd1], connect=True) self.add_pin('VSS', [vss0, vss1, vss2], connect=True) tidx_list = self.get_available_tracks(vm_layer, 0, inv_in_tr, 0, self.bound_box.yh, width=vm_w_sup, sep=vm_sep_sup) for tidx in tidx_list: tid = TrackID(vm_layer, tidx, vm_w_sup) for wire in vdd_hm + [vss_hm[1], vdd0, vdd1, vss1]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.MIDDLE) for wire in [vss_hm[0], vss0]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.UPPER) for wire in [vss_hm[2], vss2]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.LOWER) if self.params['export_dc_in']: self.add_pin('a_in_buf', a_in_buf_vm) if self.params['export_dc_out']: self.add_pin('b_in', b_in_vm) # Set the schematic parameters self.sch_params = dict( pi_params=pi_master.sch_params, dc_params=dc_master.sch_params, inv_params=inv_sch_params, nand_params=nand_sch_params, num_core=ncores, export_dc_out=self.params['export_dc_out'], export_dc_in=self.params['export_dc_in'], )