def draw_layout(self): # type: () -> None top_layer = self.params['top_layer'] bound_box = self.params['bound_box'] w = self.params['w'] fg_side = self.params['fg_side'] threshold = self.params['threshold'] show_pins = self.params['show_pins'] dnw_mode = self.params['dnw_mode'] # test top_layer hm_layer = self._tech_cls.get_mos_conn_layer() + 1 if top_layer <= hm_layer: raise ValueError('top layer for DeepNWellRing must be >= %d' % (hm_layer + 1)) # make masters dnw_params = dict( top_layer=top_layer, bound_box=bound_box, sub_type='ntap', w=w, fg_side=fg_side, threshold=threshold, show_pins=False, dnw_mode='compact', ) dnw_master = self.new_template(params=dnw_params, temp_cls=SubstrateRing) dnw_blk_loc = dnw_master.blk_loc_unit sub_params = dict( top_layer=top_layer, bound_box=dnw_master.bound_box, sub_type='ptap', w=w, fg_side=fg_side, threshold=threshold, show_pins=False, ) sub_master = self.new_template(params=sub_params, temp_cls=SubstrateRing) sub_blk_loc = sub_master.blk_loc_unit # put masters at (0, 0) sub_inst = self.add_instance(sub_master, 'XSUB') dnw_inst = self.add_instance(dnw_master, 'XDNW', loc=sub_blk_loc, unit_mode=True) # check how much to move substrate rings by to achive the DNW margin. x_pitch, y_pitch = self.grid.get_block_size(top_layer, unit_mode=True) dnw_margin = self.grid.tech_info.get_dnw_margin_unit(dnw_mode) dnw_box = BBox.get_invalid_bbox() for dnw_lay in self.grid.tech_info.get_dnw_layers(): dnw_box = dnw_box.merge(dnw_inst.get_rect_bbox(dnw_lay)) dx = -(-max(0, dnw_margin - dnw_box.left_unit) // x_pitch) * x_pitch dy = -(-max(0, dnw_margin - dnw_box.bottom_unit) // x_pitch) * x_pitch self.move_all_by(dx=dx, dy=dy, unit_mode=True) # set size res = self.grid.resolution sub_w = sub_master.bound_box.width_unit sub_h = sub_master.bound_box.height_unit bnd_box = BBox(0, 0, sub_w + 2 * dx, sub_h + 2 * dy, res, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.array_box = bnd_box # record block location dnw_loc = dnw_inst.location_unit self._blk_loc = dnw_loc[0] + dnw_blk_loc[0], dnw_loc[1] + dnw_blk_loc[1] # export supplies self.reexport(sub_inst.get_port('VSS'), show=show_pins) self.reexport(dnw_inst.get_port('VDD'), show=show_pins)
def draw_layout(self): # type: () -> None nin0 = self.params['nin0'] nin1 = self.params['nin1'] res_params = self.params['res_params'] mux_params = self.params['mux_params'] fill_config = self.params['fill_config'] nout = self.params['nout'] top_layer = self.params['top_layer'] fill_orient_mode = self.params['fill_orient_mode'] show_pins = self.params['show_pins'] if nout <= 0: raise ValueError('nout must be positive.') res = self.grid.resolution num_mux_left = nout // 2 num_mux_right = nout - num_mux_left num_col = 2**nin0 num_row = 2**nin1 nbits_tot = nin0 + nin1 # make masters r_params = res_params.copy() r_params['nx'] = num_col r_params['ny'] = num_row r_params['show_pins'] = False res_master = self.new_template(params=r_params, temp_cls=ResLadderTop) sup_layer = res_master.top_layer + 1 if top_layer is None: top_layer = res_master.top_layer + 1 elif top_layer < sup_layer: raise ValueError('top_layer must be >= %d' % sup_layer) m_params = mux_params.copy() m_params['col_nbits'] = nin0 m_params['row_nbits'] = nin1 m_params['top_layer'] = res_master.top_layer m_params['show_pins'] = False if num_mux_left > 0: m_params['num_mux'] = num_mux_left lmux_master = self.new_template(params=m_params, temp_cls=RLadderMuxArray) else: lmux_master = None m_params['num_mux'] = num_mux_right rmux_master = self.new_template(params=m_params, temp_cls=RLadderMuxArray) # figure out Y coordinates mux_warr = rmux_master.get_port('in<1>').get_pins()[0] pin_layer = mux_warr.layer_id tr_pitch = self.grid.get_track_pitch(pin_layer, unit_mode=True) mux_tr = mux_warr.track_id.base_index res_tr = res_master.get_port( 'out<1>').get_pins()[0].track_id.base_index res_yo = int(round(max(mux_tr - res_tr, 0))) * tr_pitch mux_yo = int(round(max(res_tr - mux_tr, 0))) * tr_pitch # place left mux sup_table = {'VDD': [], 'VSS': []} if lmux_master is not None: blk_w, blk_h = self.grid.get_size_dimension(lmux_master.size, unit_mode=True) lmux_inst = self.add_instance(lmux_master, loc=(blk_w, mux_yo), orient='MY', unit_mode=True) # gather supply and re-export inputs for port_name, port_list in sup_table.items(): port_list.extend(lmux_inst.port_pins_iter(port_name)) for mux_idx in range(num_mux_left): self.reexport(lmux_inst.get_port('out<%d>' % mux_idx), show=show_pins) for bit_idx in range(nbits_tot): self.reexport(lmux_inst.get_port( 'code<%d>' % (bit_idx + mux_idx * nbits_tot)), show=show_pins) vref_left = int( round(lmux_inst.get_port('in<1>').get_pins()[0].lower / res)) xo = blk_w self.array_box = lmux_inst.array_box else: xo = 0 vref_left = -1 self.array_box = BBox.get_invalid_bbox() # place resistor ladder res_inst = self.add_instance(res_master, loc=(xo, res_yo), unit_mode=True) for port_name, port_list in sup_table.items(): port_list.extend(res_inst.port_pins_iter(port_name)) if vref_left < 0: vref_left = int( round(res_inst.get_port('out<1>').get_pins()[0].lower / res)) res_w, res_h = self.grid.get_size_dimension(res_master.size, unit_mode=True) xo += res_w # place right mux rmux_inst = self.add_instance(rmux_master, loc=(xo, mux_yo), unit_mode=True) rmux_w, rmux_h = self.grid.get_size_dimension(rmux_master.size, unit_mode=True) xo += rmux_w out_off = num_mux_left in_off = num_mux_left * nbits_tot for port_name, port_list in sup_table.items(): port_list.extend(rmux_inst.port_pins_iter(port_name)) for mux_idx in range(num_mux_right): old_name = 'out<%d>' % mux_idx new_name = 'out' if nout == 1 else 'out<%d>' % (mux_idx + out_off) self.reexport(rmux_inst.get_port(old_name), net_name=new_name, show=show_pins) for bit_idx in range(nbits_tot): old_name = 'code<%d>' % (bit_idx + mux_idx * nbits_tot) new_name = 'code<%d>' % (bit_idx + mux_idx * nbits_tot + in_off) self.reexport(rmux_inst.get_port(old_name), net_name=new_name, show=show_pins) vref_right = int( round(rmux_inst.get_port('in<1>').get_pins()[0].upper / res)) for vref_idx in range(2**nbits_tot): vref_warr = rmux_inst.get_port('in<%d>' % vref_idx).get_pins()[0] vref_tr = vref_warr.track_id.base_index vref_layer = vref_warr.track_id.layer_id self.add_wires(vref_layer, vref_tr, vref_left, vref_right, unit_mode=True) # set size yo = max(mux_yo + rmux_h, res_yo + res_h) blk_w, blk_h = self.grid.get_fill_size(top_layer, fill_config, unit_mode=True) nfill_x = -(-xo // blk_w) # type: int nfill_y = -(-yo // blk_h) bnd_box = BBox(0, 0, nfill_x * blk_w, nfill_y * blk_h, res, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.array_box = bnd_box self.add_cell_boundary(bnd_box) # connect supplies vdd_list = sup_table['VDD'] vss_list = sup_table['VSS'] flip_fill = (fill_orient_mode & 2 != 0) fill_width, fill_space, space, space_le = fill_config[sup_layer] vdd_list, vss_list = self.do_power_fill(sup_layer, space, space_le, vdd_warrs=vdd_list, vss_warrs=vss_list, fill_width=fill_width, fill_space=fill_space, flip=flip_fill, unit_mode=True) # add fill cells if top_layer > sup_layer: fill_params = dict( fill_config=fill_config, bot_layer=sup_layer, top_layer=top_layer, show_pins=False, ) orient = PowerFill.get_fill_orient(fill_orient_mode) x0 = 0 if (fill_orient_mode & 1 == 0) else 1 y0 = 0 if (fill_orient_mode & 2 == 0) else 1 loc = (x0 * blk_w, y0 * blk_h) fill_master = self.new_template(params=fill_params, temp_cls=PowerFill) fill_inst = self.add_instance(fill_master, 'XFILL', loc=loc, orient=orient, nx=nfill_x, ny=nfill_y, spx=blk_w, spy=blk_h, unit_mode=True) vdd_list = fill_inst.get_all_port_pins('VDD') vss_list = fill_inst.get_all_port_pins('VSS') self.add_pin('VDD', self.connect_wires(vdd_list), show=show_pins) self.add_pin('VSS', self.connect_wires(vss_list), show=show_pins) res_sch_params = res_master.sch_params.copy() mux_sch_params = rmux_master.mux_params.copy() del res_sch_params['nout'] del mux_sch_params['nin0'] del mux_sch_params['nin1'] self._sch_params = dict( nin0=nin0, nin1=nin1, nout=nout, res_params=res_sch_params, mux_params=mux_sch_params, )
def draw_layout(self): # type: () -> None top_layer = self.params['top_layer'] lch = self.params['lch'] w = self.params['w'] sub_type = self.params['sub_type'] threshold = self.params['threshold'] port_width = self.params['port_width'] well_width = self.params['well_width'] end_mode = self.params['end_mode'] is_passive = self.params['is_passive'] max_nxblk = self.params['max_nxblk'] port_tid = self.params['port_tid'] show_pins = self.params['show_pins'] half_blk_y = self.params['half_blk_y'] half_blk_x = self.params['half_blk_x'] res = self.grid.resolution well_width = int(round(well_width / res)) right_end = (end_mode & 8) != 0 left_end = (end_mode & 4) != 0 top_end = (end_mode & 2) != 0 bot_end = (end_mode & 1) != 0 # get layout info, also set RoutingGrid to substrate grid. layout_info = AnalogBaseInfo(self.grid, lch, 0, top_layer=top_layer, end_mode=end_mode, half_blk_y=half_blk_y, half_blk_x=half_blk_x) # compute template width in number of sd pitches # find maximum number of fingers we can draw bin_iter = BinaryIterator(1, None) while bin_iter.has_next(): cur_fg = bin_iter.get_next() cur_pinfo = layout_info.get_placement_info(cur_fg) cur_core_width = cur_pinfo.core_width if cur_core_width == well_width: bin_iter.save_info(cur_pinfo) break elif cur_core_width < well_width: bin_iter.save_info(cur_pinfo) bin_iter.up() else: bin_iter.down() sub_fg_tot = bin_iter.get_last_save() if sub_fg_tot is None: raise ValueError('Cannot draw substrate that fit in width: %d' % well_width) # check width parity requirement if max_nxblk > 0: blkw = self.grid.get_block_size(top_layer, unit_mode=True)[0] place_info = bin_iter.get_last_save_info() cur_nxblk = place_info.tot_width // blkw while sub_fg_tot > 0 and (cur_nxblk > max_nxblk or (max_nxblk - cur_nxblk) % 2 != 0): sub_fg_tot -= 1 place_info = layout_info.get_placement_info(sub_fg_tot) cur_nxblk = place_info.tot_width // blkw if sub_fg_tot <= 0: raise ValueError('Cannot draw substrate with width = %d, ' 'max_nxblk = %d' % (well_width, max_nxblk)) layout_info.set_fg_tot(sub_fg_tot) self.grid = layout_info.grid place_info = layout_info.get_placement_info(sub_fg_tot) edgel_x0 = place_info.edge_margins[0] tot_width = place_info.tot_width # create masters master_list = [ self.new_template(params=dict( lch=lch, fg=sub_fg_tot, sub_type=sub_type, threshold=threshold, is_end=bot_end, top_layer=top_layer, ), temp_cls=AnalogEndRow), self.new_template(params=dict( lch=lch, w=w, sub_type=sub_type, threshold=threshold, fg=sub_fg_tot, top_layer=top_layer, options=dict(is_passive=is_passive), ), temp_cls=AnalogSubstrate), self.new_template(params=dict( lch=lch, fg=sub_fg_tot, sub_type=sub_type, threshold=threshold, is_end=top_end, top_layer=top_layer, ), temp_cls=AnalogEndRow), ] ycur = 0 array_box = BBox.get_invalid_bbox() sub_conn, inst = None, None for master, orient in zip(master_list, ['R0', 'R0', 'MX']): if orient == 'MX': ycur += master.array_box.top_unit name_id = master.get_layout_basename() edge_layout_info = master.get_edge_layout_info() xcur = edgel_x0 if left_end: edge_info = master.get_left_edge_info() edge_params = dict( is_end=True, guard_ring_nf=0, name_id=name_id, layout_info=edge_layout_info, adj_blk_info=edge_info, ) edge_master = self.new_template(params=edge_params, temp_cls=AnalogEdge) if not edge_master.is_empty: edge_inst = self.add_instance(edge_master, loc=(edgel_x0, ycur), orient=orient, unit_mode=True) array_box = array_box.merge(edge_inst.array_box) xcur = edge_inst.array_box.right_unit inst = self.add_instance(master, loc=(xcur, ycur), orient=orient, unit_mode=True) array_box = array_box.merge(inst.array_box) if isinstance(master, AnalogSubstrate): conn_params = dict( layout_info=edge_layout_info, layout_name=name_id + '_subconn', is_laygo=False, ) conn_master = self.new_template(params=conn_params, temp_cls=AnalogSubstrateConn) sub_conn = self.add_instance(conn_master, loc=(xcur, ycur), orient=orient, unit_mode=True) xcur = inst.array_box.right_unit if right_end: edge_info = master.get_right_edge_info() edge_params = dict( is_end=True, guard_ring_nf=0, name_id=name_id, layout_info=edge_layout_info, adj_blk_info=edge_info, ) edge_master = self.new_template(params=edge_params, temp_cls=AnalogEdge) if not edge_master.is_empty: xcur += edge_master.array_box.right_unit eor = 'MY' if orient == 'R0' else 'R180' edge_inst = self.add_instance(edge_master, loc=(xcur, ycur), orient=eor, unit_mode=True) array_box = array_box.merge(edge_inst.array_box) if orient == 'R0': ycur += master.array_box.top_unit # calculate substrate Y coordinates imp_yb, thres_yb = master_list[0].sub_ysep imp_yt, thres_yt = master_list[2].sub_ysep self._sub_bndy = (imp_yb, ycur - imp_yt), (thres_yb, ycur - thres_yt) # get left/right substrate coordinates tot_imp_box = BBox.get_invalid_bbox() for lay in self.grid.tech_info.get_implant_layers('ptap'): tot_imp_box = tot_imp_box.merge(self.get_rect_bbox(lay)) for lay in self.grid.tech_info.get_implant_layers('ntap'): tot_imp_box = tot_imp_box.merge(self.get_rect_bbox(lay)) if not tot_imp_box.is_physical(): self._sub_bndx = None, None else: self._sub_bndx = tot_imp_box.left_unit, tot_imp_box.right_unit # set array box and size self.array_box = array_box bound_box = BBox(0, 0, tot_width, inst.bound_box.top_unit, res, unit_mode=True) if self.grid.size_defined(top_layer): self.set_size_from_bound_box(top_layer, bound_box) else: self.prim_bound_box = bound_box self.prim_top_layer = top_layer hm_layer = layout_info.mconn_port_layer + 1 if port_tid is None: # find center track index hm_mid = self.grid.coord_to_nearest_track(hm_layer, self.array_box.yc_unit, mode=0, half_track=True, unit_mode=True) # connect to horizontal metal layer. hm_pitch = self.grid.get_track_pitch(hm_layer, unit_mode=True) ntr = self.array_box.height_unit // hm_pitch # type: int if port_width is None: port_width = self.grid.get_max_track_width( hm_layer, 1, ntr, half_end_space=False) port_tid = TrackID(hm_layer, hm_mid, width=port_width) else: port_tid = TrackID(hm_layer, port_tid[0], width=port_tid[1]) port_name = 'VDD' if sub_type == 'ntap' else 'VSS' sub_wires = self.connect_to_tracks( sub_conn.get_port(port_name).get_pins(hm_layer - 1), port_tid) self.add_pin(port_name, sub_wires, show=show_pins) self._fg_tot = sub_fg_tot
def get_track_bbox(self, layer_id): # type: (int) -> BBox if layer_id not in self._idx_table: return BBox.get_invalid_bbox() return self._idx_table[layer_id].bound_box
def _draw_layout_helper( self, # type: ResLadderDAC l, # type: float w, # type: float sub_lch, # type: float sub_w, # type: Union[float, int] sub_type, # type: str threshold, # type: str ndum, # type: int res_type, # type: str num_out, # type: int col_nbits, # type: int row_nbits, # type: int config_file, # type: str **kwargs): # type: (...) -> None if num_out <= 0: raise ValueError('num_out must be positive.') res = self.grid.resolution num_mux_left = num_out // 2 num_mux_right = num_out - num_mux_left num_col = 2**col_nbits num_row = 2**row_nbits nbits_tot = col_nbits + row_nbits # make masters mux_params = dict(col_nbits=col_nbits, row_nbits=row_nbits, config_file=config_file) if num_mux_left > 0: mux_params['num_mux'] = num_mux_left lmux_master = self.new_template(params=mux_params, temp_cls=RLadderMuxArray) else: lmux_master = None mux_params['num_mux'] = num_mux_right rmux_master = self.new_template(params=mux_params, temp_cls=RLadderMuxArray) res_params = dict(l=l, w=w, sub_lch=sub_lch, sub_w=sub_w, sub_type=sub_type, threshold=threshold, nx=num_col, ny=num_row, ndum=ndum, res_type=res_type) res_master = self.new_template(params=res_params, temp_cls=ResLadder) # figure out Y coordinates mux_warr = rmux_master.get_port('in<1>').get_pins()[0] pin_layer = mux_warr.layer_id tr_pitch = self.grid.get_track_pitch(pin_layer, unit_mode=True) mux_tr = mux_warr.track_id.base_index res_tr = res_master.get_port( 'out<1>').get_pins()[0].track_id.base_index res_yo = int(round(max(mux_tr - res_tr, 0))) * tr_pitch mux_yo = int(round(max(res_tr - mux_tr, 0))) * tr_pitch # place left mux sup_table = {'VDD': [], 'VSS': []} if lmux_master is not None: blk_w, blk_h = self.grid.get_size_dimension(lmux_master.size, unit_mode=True) lmux_inst = self.add_instance(lmux_master, loc=(blk_w, mux_yo), orient='MY', unit_mode=True) # gather supply and re-export inputs for port_name, port_list in sup_table.items(): port_list.extend(lmux_inst.get_all_port_pins(port_name)) for mux_idx in range(num_mux_left): self.reexport(lmux_inst.get_port('out<%d>' % mux_idx), show=True) for bit_idx in range(nbits_tot): self.reexport(lmux_inst.get_port( 'code<%d>' % (bit_idx + mux_idx * nbits_tot)), show=True) vref_left = int( round(lmux_inst.get_port('in<1>').get_pins()[0].lower / res)) xo = blk_w self.array_box = lmux_inst.array_box else: xo = 0 vref_left = -1 self.array_box = BBox.get_invalid_bbox() # place resistor ladder res_inst = self.add_instance(res_master, loc=(xo, res_yo), unit_mode=True) for port_name, port_list in sup_table.items(): port_list.extend(res_inst.get_all_port_pins(port_name)) if vref_left < 0: vref_left = int( round(res_inst.get_port('out<1>').get_pins()[0].lower / res)) res_w, res_h = self.grid.get_size_dimension(res_master.size, unit_mode=True) xo += res_w # place right mux rmux_inst = self.add_instance(rmux_master, loc=(xo, mux_yo), unit_mode=True) rmux_w, rmux_h = self.grid.get_size_dimension(rmux_master.size, unit_mode=True) xo += rmux_w out_off = num_mux_left in_off = num_mux_left * nbits_tot for port_name, port_list in sup_table.items(): port_list.extend(rmux_inst.get_all_port_pins(port_name)) for mux_idx in range(num_mux_right): old_name = 'out<%d>' % mux_idx new_name = 'out' if num_out == 1 else 'out<%d>' % (mux_idx + out_off) self.reexport(rmux_inst.get_port(old_name), net_name=new_name, show=True) for bit_idx in range(nbits_tot): old_name = 'code<%d>' % (bit_idx + mux_idx * nbits_tot) new_name = 'code<%d>' % (bit_idx + mux_idx * nbits_tot + in_off) self.reexport(rmux_inst.get_port(old_name), net_name=new_name, show=True) vref_right = int( round(rmux_inst.get_port('in<1>').get_pins()[0].upper / res)) for vref_idx in range(2**nbits_tot): vref_warr = rmux_inst.get_port('in<%d>' % vref_idx).get_pins()[0] vref_tr = vref_warr.track_id.base_index vref_layer = vref_warr.track_id.layer_id self.add_wires(vref_layer, vref_tr, vref_left, vref_right, unit_mode=True) # set size yo = max(mux_yo + rmux_h, res_yo + res_h) top_layer = sup_table['VDD'][0].layer_id + 1 self.size = self.grid.get_size_tuple(top_layer, xo, yo, round_up=True, unit_mode=True) self.array_box = self.bound_box # do power fill sup_width = 2 vdd_list, vss_list = self.do_power_fill(top_layer, sup_table['VDD'], sup_table['VSS'], sup_width=sup_width, fill_margin=0.5, edge_margin=0.2) self.add_pin('VDD', vdd_list) self.add_pin('VSS', vss_list)
def draw_res_boundary(self, template, boundary_type, layout_info, end_mode): # type: (TemplateBase, str, Dict[str, Any], bool) -> None mos_layer_table = self.config['mos_layer_table'] res_layer_table = self.config['res_layer_table'] metal_exclude_table = self.config['metal_exclude_table'] layer_table = self.config['layer_name'] res_info = self.res_config['info'] imp_ency = self.res_config['imp_enc'][1] rpo_extx = self.res_config['rpo_extx'] grid = template.grid res = grid.resolution w = layout_info['w'] l = layout_info['l'] res_type = layout_info['res_type'] sub_type = layout_info['sub_type'] threshold = layout_info['threshold'] w_core = layout_info['w_core'] h_core = layout_info['h_core'] w_edge = layout_info['w_edge'] h_edge = layout_info['h_edge'] res_info = res_info[res_type] need_rpo = res_info['need_rpo'] od_in_res = res_info['od_in_res'] wres, lres, wres_lr, lres_tb = self.get_res_dimension(l, w) implant_layers = self.get_res_imp_layers(res_type, sub_type, threshold) bnd_spx = (w_core - wres) // 2 bnd_spy = (h_core - lres) // 2 core_info = layout_info['core_info'] edge_lr_info = layout_info['edge_lr_info'] edge_tb_info = layout_info['edge_tb_info'] well_xl = edge_lr_info['well_xl'] well_yb = edge_tb_info['well_yb'] core_lr_od_xloc = core_info['lr_od_xloc'] core_top_od_yloc = core_info['top_od_yloc'] core_bot_od_yloc = core_info['bot_od_yloc'] # get bounding box/implant coordinates, draw RPDMY/RPO, and draw dummies. po_xl = po_xr = po_yb = po_yt = None if boundary_type == 'lr': # set implant Y coordinates to 0 well_yb = 0 # get bounding box and PO coordinates bnd_box = BBox(0, 0, w_edge, h_core, res, unit_mode=True) if wres_lr > 0: po_xr = w_edge - bnd_spx po_xl = po_xr - wres_lr po_yb, po_yt = bnd_spy, bnd_spy + lres # draw bottom/top edge dummies po_x_list = [(po_xl, po_xr)] self._draw_dummies(template, po_x_list, core_bot_od_yloc) self._draw_dummies(template, po_x_list, core_top_od_yloc) if need_rpo: # draw RPO in left/right edge block rpo_yb = h_core // 2 - l // 2 rpo_yt = rpo_yb + l rpo_name = res_layer_table['RPO'] rpo_xl = w_edge + bnd_spx - rpo_extx rpo_box = BBox(rpo_xl, rpo_yb, w_edge, rpo_yt, res, unit_mode=True) if rpo_box.is_physical(): template.add_rect(rpo_name, rpo_box) m1_x_list = edge_lr_info['m1_edge_x'] m1_y_list = core_info['m1_core_y'] elif boundary_type == 'tb': # set implant X coordinates to 0 well_xl = 0 # get bounding box and PO coordinates bnd_box = BBox(0, 0, w_core, h_edge, res, unit_mode=True) if lres_tb > 0: po_yt = h_edge - bnd_spy po_yb = po_yt - lres_tb po_xl, po_xr = bnd_spx, bnd_spx + wres # draw bottom edge dummies po_y_list = [(po_yb, po_yt)] self._draw_dummies(template, core_lr_od_xloc, po_y_list) self._draw_dummies(template, core_lr_od_xloc, po_y_list, dx=w_core) m1_x_list = core_info['m1_core_x'] m1_y_list = edge_tb_info['m1_edge_y'] else: # get bounding box and PO coordinates bnd_box = BBox(0, 0, w_edge, h_edge, res, unit_mode=True) if wres_lr > 0 and lres_tb > 0: po_xr = w_edge - bnd_spx po_xl = po_xr - wres_lr po_yt = h_edge - bnd_spy po_yb = po_yt - lres_tb m1_x_list = edge_lr_info['m1_edge_x'] m1_y_list = edge_tb_info['m1_edge_y'] # draw DPO if wres_lr > 0 and lres_tb > 0: dpo_name = mos_layer_table['PO_dummy'] template.add_rect( dpo_name, BBox(po_xl, po_yb, po_xr, po_yt, res, unit_mode=True)) # draw implant layers if not od_in_res: if boundary_type == 'lr': po_yb, po_yt = bnd_spy, bnd_spy + lres imp_box = BBox(well_xl, po_yb - imp_ency, bnd_box.right_unit, po_yt + imp_ency, res, unit_mode=True) else: imp_box = BBox.get_invalid_bbox() else: imp_box = BBox(well_xl, well_yb, bnd_box.right_unit, bnd_box.top_unit, res, unit_mode=True) if imp_box.is_physical(): for lay in implant_layers: template.add_rect(lay, imp_box) # set bounding box template.prim_bound_box = bnd_box template.array_box = bnd_box # draw M1 exclusion layer m1_exc_layer = metal_exclude_table[1] template.add_rect(m1_exc_layer, bnd_box) # draw M1 fill m1_lay = layer_table[1] for xl, xr in m1_x_list: for yb, yt in m1_y_list: template.add_rect(m1_lay, BBox(xl, yb, xr, yt, res, unit_mode=True))
def draw_layout(self): # type: () -> None nin0 = self.params['nin0'] nin1 = self.params['nin1'] name_list2 = self.params['name_list2'] nout_list2 = self.params['nout_list2'] num_vdd_list = self.params['num_vdd_list'] fill_config = self.params['fill_config'] bias_config = self.params['bias_config'] fill_orient_mode = self.params['fill_orient_mode'] show_pins = self.params['show_pins'] # get number of VDD/VSS bias wires num_vdd_tot = num_tot = 0 for num_vdd, nout_list in zip(num_vdd_list, nout_list2): num_vdd_tot += num_vdd num_tot += sum(nout_list) num_vss_tot = num_tot - num_vdd_tot ycur = 0 inst_list = [] nout_arr_list = [] fill_info_list = [] hm_bias_info_list = [] params = self.params.copy() params['show_pins'] = False tot_box = BBox.get_invalid_bbox() top_layer = res_params = mux_params = blk_w = blk_h = None vm_layer = route_w = vdd_x = vss_x = None for row_idx, (num_vdd, nout_list) in enumerate(zip(num_vdd_list, nout_list2)): if row_idx % 2 == 0: orient = 'R0' cur_fo_mode = fill_orient_mode else: orient = 'MX' cur_fo_mode = fill_orient_mode ^ 2 params['nout_list'] = nout_list params['num_vdd'] = num_vdd params['fill_orient_mode'] = cur_fo_mode master = self.new_template(params=params, temp_cls=RDACRow) nout_arr_list.extend(master.sch_params['nout_arr_list']) if top_layer is None: top_layer = master.top_layer vm_layer = master.bias_layer - 1 res_params = master.sch_params['res_params'] mux_params = master.sch_params['mux_params'] blk_w, blk_h = self.grid.get_fill_size(top_layer, fill_config, unit_mode=True) tmp = compute_vroute_width(self, vm_layer, blk_w, num_vdd_tot, num_vss_tot, bias_config) route_w, vdd_x, vss_x = tmp ny = master.bound_box.height_unit // blk_h cur_bias_info = master.bias_info if row_idx % 2 == 1: ycur += master.bound_box.height_unit if cur_bias_info[0] is not None: num_vss, p0, dim = cur_bias_info[0] hm_bias_info_list.append((0, num_vss, ycur - p0[1] - dim)) if cur_bias_info[1] is not None: num_vdd, p0, dim = cur_bias_info[1] hm_bias_info_list.append((1, num_vdd, ycur - p0[1] - dim)) else: if cur_bias_info[1] is not None: num_vdd, p0, dim = cur_bias_info[1] hm_bias_info_list.append((1, num_vdd, p0[1] + ycur)) if cur_bias_info[0] is not None: num_vss, p0, dim = cur_bias_info[0] hm_bias_info_list.append((0, num_vss, p0[1] + ycur)) inst = self.add_instance(master, 'X%d' % row_idx, loc=(route_w, ycur), orient=orient, unit_mode=True) inst_box = inst.bound_box fill_info_list.append((inst_box.right_unit, ycur, ny, cur_fo_mode)) tot_box = tot_box.merge(inst_box) ycur = tot_box.top_unit inst_list.append(inst) self.array_box = tot_box = tot_box.extend(x=0, unit_mode=True) self.set_size_from_bound_box(top_layer, tot_box) self.add_cell_boundary(tot_box) xr_tot = tot_box.right_unit nin = nin0 + nin1 io_name_list = [] vdd_pins = [] vss_pins = [] vdd_names = [] vss_names = [] params = dict(fill_config=fill_config, bot_layer=top_layer - 1, show_pins=False) fill_master = self.new_template(params=params, temp_cls=PowerFill) for inst, (xr, yf, ny, fo_mode), name_list, num_vdd in zip( inst_list, fill_info_list, name_list2, num_vdd_list): # add fill if needed dx = xr_tot - xr if dx > 0: nx = dx // blk_w orient = PowerFill.get_fill_orient(fill_orient_mode) x0 = 0 if (fill_orient_mode & 1 == 0) else 1 y0 = 0 if (fill_orient_mode & 2 == 0) else 1 floc = (xr + x0 * blk_w, yf + y0 * blk_h) self.add_instance(fill_master, loc=floc, orient=orient, nx=nx, ny=ny, spx=blk_w, spy=blk_h, unit_mode=True) in_cnt = out_cnt = 0 for name in name_list: opin_name = 'v_%s' % name out_pin = inst.get_pin('out<%d>' % out_cnt) io_name_list.append(name) if out_cnt < num_vdd: vdd_pins.append((opin_name, out_pin)) vdd_names.append(opin_name) else: vss_pins.append((opin_name, out_pin)) vss_names.append(opin_name) for in_idx in range(nin): in_pin = inst.get_pin('code<%d>' % in_cnt) in_pin = self.extend_wires(in_pin, upper=xr_tot, unit_mode=True) self.add_pin('bias_%s<%d>' % (name, in_idx), in_pin, show=show_pins, edge_mode=1) in_cnt += 1 out_cnt += 1 # draw routes tmp = join_bias_vroutes(self, vm_layer, vdd_x, vss_x, route_w, num_vdd_tot, num_vss_tot, hm_bias_info_list, bias_config, vdd_pins, vss_pins, yt=self.bound_box.top_unit) vdd_pins, vss_pins, vdd_list, vss_list = tmp for name, warr in chain(vdd_pins, vss_pins): self.add_pin(name, warr, show=show_pins, edge_mode=1) # draw fill over routes nx = route_w // blk_w ny = tot_box.top_unit // blk_h orient = PowerFill.get_fill_orient(fill_orient_mode) dx = 0 if (fill_orient_mode & 1 == 0) else 1 dy = 0 if (fill_orient_mode & 2 == 0) else 1 loc = (dx * blk_w, dy * blk_h) inst = self.add_instance(fill_master, loc=loc, orient=orient, nx=nx, ny=ny, spx=blk_w, spy=blk_h, unit_mode=True) if vdd_list: self.draw_vias_on_intersections(vdd_list, inst.get_all_port_pins('VDD_b')) if vss_list: self.draw_vias_on_intersections(vss_list, inst.get_all_port_pins('VSS_b')) self.reexport(inst.get_port('VDD'), show=show_pins) self.reexport(inst.get_port('VSS'), show=show_pins) self._sch_params = dict( nin0=nin0, nin1=nin1, nout_arr_list=nout_arr_list, res_params=res_params, mux_params=mux_params, io_name_list=io_name_list, ) self._bias_info = ((vdd_x[0], vdd_names), (vss_x[0], vss_names))