def draw_dum_connection_helper( self, template, # type: TemplateBase lch_unit, # type: int fg, # type: int sd_pitch, # type: int xc, # type: int od_y, # type: Tuple[int, int] md_y, # type: Tuple[int, int] ds_x_list, # type: List[int] gate_tracks, # type: List[Union[float, int]] left_edge, # type: bool right_edge, # type: bool options, # type: Dict[str, Any] ): # type: (...) -> List[WireArray] res = self.res lay_name_table = self.config['layer_name'] mos_constants = self.get_mos_tech_constants(lch_unit) g_m1_dum_h = mos_constants['g_m1_dum_h'] conn_yloc_info = self.get_conn_yloc_info(lch_unit, od_y, md_y, False) m1_lay = lay_name_table[1] m1_yb = conn_yloc_info['g_y_list'][0][0] m1_yt = conn_yloc_info['d_y_list'][0][1] # draw gate/drain/source connection to M1 self.draw_g_connection(template, lch_unit, fg, sd_pitch, xc, od_y, md_y, [], is_sub=False, is_dum=True) self.draw_ds_connection(template, lch_unit, fg, sd_pitch, xc, od_y, md_y, [], [], False, 1, 1, is_dum=True) self.draw_ds_connection(template, lch_unit, fg, sd_pitch, xc, od_y, md_y, [], [], True, 1, 2, is_dum=True) # short M1 together dum_layer = self.get_dum_conn_layer() for wire_xc in ds_x_list: tidx = template.grid.coord_to_track(dum_layer, wire_xc, unit_mode=True) template.add_wires(dum_layer, tidx, m1_yb, m1_yt, unit_mode=True) xl = ds_x_list[0] xr = ds_x_list[-1] if xr > xl: template.add_rect( m1_lay, BBox(xl, m1_yb, xr, m1_yb + g_m1_dum_h, res, unit_mode=True)) # return gate ports return [ WireArray(TrackID(dum_layer, tidx), m1_yb * res, m1_yt * res, res) for tidx in gate_tracks ]
def draw_ds_connection( self, # type: MOSTechCDSFFMPT template, # type: TemplateBase lch_unit, # type: int fg, # type: int wire_pitch, # type: int xc, # type: int od_y, # type: Tuple[int, int] md_y, # type: Tuple[int, int] dum_x_list, # type: List[int] conn_x_list, # type: List[int] align_gate, # type: bool wire_dir, # type: int ds_code, # type: int **kwargs): # type: (...) -> Tuple[List[WireArray], List[WireArray]] is_dum = kwargs.get('is_dum', False) res = self.res mos_lay_table = self.config['mos_layer_table'] mos_constants = self.get_mos_tech_constants(lch_unit) md_w = mos_constants['md_w'] bot_layer = mos_constants['d_bot_layer'] via_info = mos_constants['d_via'] is_sub = (ds_code == 3) conn_yloc_info = self.get_conn_yloc_info(lch_unit, od_y, md_y, is_sub) conn_drc_info = self.get_conn_drc_info(lch_unit, 'd') dum_layer = self.get_dum_conn_layer() mos_layer = self.get_mos_conn_layer() dum_warrs, conn_warrs = [], [] # figure out via X coordinates if is_sub: via_x_list = list(range(xc, xc + (fg + 1) * wire_pitch, wire_pitch)) conn_y_list = conn_yloc_info['d_y_list'] else: if ds_code == 1: via_x_list = list( range(xc, xc + (fg + 1) * wire_pitch, 2 * wire_pitch)) else: via_x_list = list( range(xc + wire_pitch, xc + (fg + 1) * wire_pitch, 2 * wire_pitch)) if align_gate: conn_y_list = conn_yloc_info['d_y_list'] else: conn_y_list = conn_yloc_info['s_y_list'] # connect from OD up to M3 stop_layer = dum_layer if is_dum else mos_layer lay_list = range(bot_layer, stop_layer + 1) prev_info = md_y[0], md_y[1], 'y', md_w, mos_lay_table['MD'] for cur_lay, cur_y, via_dim, via_sp, via_ble, via_tle in \ zip(lay_list, conn_y_list, via_info['dim'], via_info['sp'], via_info['bot_enc_le'], via_info['top_enc_le']): prev_info = self.up_one_layer(template, cur_lay, cur_y, via_dim, via_sp, via_ble, via_tle, via_x_list, prev_info, conn_drc_info) # add WireArrays if stop_layer >= dum_layer: cur_yb, cur_yt = conn_y_list[dum_layer - bot_layer] for conn_xc in dum_x_list: tidx = template.grid.coord_to_track(dum_layer, conn_xc, unit_mode=True) dum_warrs.append( WireArray(TrackID(dum_layer, tidx), cur_yb * res, cur_yt * res, res)) if stop_layer >= mos_layer: cur_yb, cur_yt = conn_y_list[mos_layer - bot_layer] for conn_xc in conn_x_list: tidx = template.grid.coord_to_track(mos_layer, conn_xc, unit_mode=True) conn_warrs.append( WireArray(TrackID(mos_layer, tidx), cur_yb * res, cur_yt * res, res)) return dum_warrs, conn_warrs
def draw_g_connection( self, # type: MOSTechCDSFFMPT template, # type: TemplateBase lch_unit, # type: int fg, # type: int sd_pitch, # type: int xc, # type: int od_y, # type: Tuple[int, int] md_y, # type: Tuple[int, int] conn_x_list, # type: List[int] is_sub=False, # type: bool **kwargs): # type: (...) -> List[WireArray] is_dum = kwargs.get('is_dum', False) res = self.res mos_lay_table = self.config['mos_layer_table'] lay_name_table = self.config['layer_name'] via_id_table = self.config['via_id'] mos_constants = self.get_mos_tech_constants(lch_unit) mp_po_ovl_constants = mos_constants['mp_po_ovl_constants'] mp_po_ovl_constants_sub = mos_constants['mp_po_ovl_constants_sub'] mp_h = mos_constants['mp_h'] mp_h_sub = mos_constants['mp_h_sub'] via_info = mos_constants['g_via'] conn_yloc_info = self.get_conn_yloc_info(lch_unit, od_y, md_y, is_sub) conn_drc_info = self.get_conn_drc_info(lch_unit, 'g') conn_warrs = [] mp_lay = mos_lay_table['MP'] m1_w = conn_drc_info[1]['w'] mp_y_list = conn_yloc_info['mp_y_list'] v0_id = via_id_table[(mos_lay_table['MP'], lay_name_table[1])] if is_sub: mp_po_ovl = mp_po_ovl_constants_sub[ 0] + lch_unit * mp_po_ovl_constants_sub[1] # connect gate to M1 only m1_yb, m1_yt = conn_yloc_info['d_y_list'][0] via_w, via_h = via_info['dim'][0] bot_encx = via_info['bot_enc_le'][0] top_encx = (m1_w - via_w) // 2 bot_ency = (mp_h_sub - via_h) // 2 top_ency = via_info['top_enc_le'][0] mp_dx = sd_pitch // 2 - lch_unit // 2 + mp_po_ovl enc1 = [bot_encx, bot_encx, bot_ency, bot_ency] enc2 = [top_encx, top_encx, top_ency, top_ency] for via_xc in range(xc, xc + (fg + 1) * sd_pitch, 2 * sd_pitch): mp_xl = via_xc - mp_dx mp_xr = via_xc + mp_dx for mp_yb, mp_yt in mp_y_list: template.add_rect( mp_lay, BBox(mp_xl, mp_yb, mp_xr, mp_yt, res, unit_mode=True)) mp_yc = (mp_yb + mp_yt) // 2 template.add_via_primitive(v0_id, [via_xc, mp_yc], enc1=enc1, enc2=enc2, cut_width=via_w, cut_height=via_h, unit_mode=True) template.add_rect( 'M1', BBox(via_xc - m1_w // 2, m1_yb, via_xc + m1_w // 2, m1_yt, res, unit_mode=True)) else: mp_po_ovl = mp_po_ovl_constants[ 0] + lch_unit * mp_po_ovl_constants[1] if fg % 2 == 0: gate_fg_list = [2] * (fg // 2) else: if fg == 1: raise ValueError('cannot connect 1 finger transistor') if fg <= 5: gate_fg_list = [fg] else: num_mp_half = (fg - 3) // 2 gate_fg_list = list( chain(repeat(2, num_mp_half), [3], repeat(2, num_mp_half))) # connect gate to M1. tot_fg = 0 mp_yb, mp_yt = mp_y_list[0] m1_yb, m1_yt = conn_yloc_info['g_y_list'][0] via_w, via_h = via_info['dim'][0] bot_encx = via_info['bot_enc_le'][0] top_encx = (m1_w - via_w) // 2 bot_ency = (mp_h - via_h) // 2 top_ency = via_info['top_enc_le'][0] enc1 = [bot_encx, bot_encx, bot_ency, bot_ency] enc2 = [top_encx, top_encx, top_ency, top_ency] via_yc = (mp_yb + mp_yt) // 2 via_x_list = [] for num_fg in gate_fg_list: via_xoff = xc + (tot_fg + 1) * sd_pitch cur_xc = xc + tot_fg * sd_pitch + num_fg * sd_pitch // 2 # draw MP mp_w = (num_fg - 1) * sd_pitch - lch_unit + 2 * mp_po_ovl mp_xl = cur_xc - mp_w // 2 mp_xr = mp_xl + mp_w template.add_rect( mp_lay, BBox(mp_xl, mp_yb, mp_xr, mp_yt, res, unit_mode=True)) # draw V0, M1 for via_xc in range(via_xoff, via_xoff + (num_fg - 1) * sd_pitch, sd_pitch): cur_tidx = template.grid.coord_to_track(1, via_xc, unit_mode=True) template.add_via_primitive(v0_id, [via_xc, via_yc], enc1=enc1, enc2=enc2, cut_width=via_w, cut_height=via_h, unit_mode=True) template.add_wires(1, cur_tidx, m1_yb, m1_yt, unit_mode=True) via_x_list.append(via_xc) tot_fg += num_fg # connect from M1 up to M3 if not dummy gate connection if not is_dum: conn_y_list = conn_yloc_info['g_y_list'][1:] lay_list = range(2, 2 + len(conn_y_list)) prev_info = m1_yb, m1_yt, 'y', m1_w, lay_name_table[1] for cur_lay, cur_y, via_dim, via_sp, via_ble, via_tle in \ zip(lay_list, conn_y_list, via_info['dim'][1:], via_info['sp'][1:], via_info['bot_enc_le'][1:], via_info['top_enc_le'][1:]): prev_info = self.up_one_layer(template, cur_lay, cur_y, via_dim, via_sp, via_ble, via_tle, via_x_list, prev_info, conn_drc_info) via_x_list = conn_x_list # add ports mos_layer = self.get_mos_conn_layer() cur_yb, cur_yt = conn_y_list[-1] for conn_xc in conn_x_list: tidx = template.grid.coord_to_track(mos_layer, conn_xc, unit_mode=True) conn_warrs.append( WireArray(TrackID(mos_layer, tidx), cur_yb * res, cur_yt * res, res)) return conn_warrs
def _draw_layout_helper(self, io_names, sup_name, reserve_tracks, bus_layer, bus_margin, show_pins, track_width): # compute bus length track_space = self.grid.get_num_space_tracks(bus_layer, width_ntr=track_width) io_names = {name: idx for idx, name in enumerate(io_names)} bus_lower = None bus_upper = None reserve_list = [] io_dict = {bus_layer - 1: [], bus_layer + 1: []} for name, layer, track, width in reserve_tracks: if layer == bus_layer - 1 or layer == bus_layer + 1: reserve_list.append((layer, track, width)) num_space = self.grid.get_num_space_tracks(layer, width_ntr=width) lower = self.grid.get_wire_bounds(layer, track - num_space, width=width, unit_mode=True)[0] upper = self.grid.get_wire_bounds(layer, track + num_space, width=width, unit_mode=True)[1] if bus_lower is None: bus_lower, bus_upper = lower, upper else: bus_lower = min(bus_lower, lower) bus_upper = max(bus_upper, upper) idx = io_names.get(name, -1) if idx >= 0: io_dict[layer].append((name, idx, track, width)) bus_upper = self._get_bound(bus_upper, bus_layer, 1) # draw input buses track_pitch = track_width + track_space track0 = bus_margin + track_space + (track_width + 1) / 2 wire_layers = (bus_layer - 1, bus_layer + 1) for lay in wire_layers: for name, idx, track, width in io_dict[lay]: mid = self.grid.track_to_coord(lay, track, unit_mode=True) cur_tr_idx = track0 + idx * track_pitch w = self.add_wires(bus_layer, cur_tr_idx, 0, mid, width=track_width, unit_mode=True) w_in = self.connect_to_tracks(w, TrackID(lay, track, width=width), min_len_mode=0) pin_w = WireArray( TrackID(bus_layer, cur_tr_idx, width=track_width), 0, self.grid.get_min_length(bus_layer, 1)) self.add_pin(name, pin_w, show=show_pins) self.add_pin(name + '_in', w_in, show=show_pins) # compute size last_sup_track = track0 + (len(io_names) - 1) * track_pitch + ( track_width + 1) / 2 + track_space bus_wl = self.grid.get_wire_bounds(bus_layer, bus_margin, unit_mode=True)[0] bus_wu = self.grid.get_wire_bounds(bus_layer, last_sup_track, unit_mode=True)[1] size_wu = self.grid.get_wire_bounds(bus_layer, last_sup_track + bus_margin, unit_mode=True)[1] if self.grid.get_direction(bus_layer) == 'x': cur_width, cur_height = bus_upper, size_wu else: cur_width, cur_height = size_wu, bus_upper self.size = self.grid.get_size_tuple(bus_layer + 2, cur_width, cur_height, round_up=True, unit_mode=True) self.array_box = self.bound_box # reserve tracks for lay, track, width in reserve_list: self.reserve_tracks(lay, track, width) # draw supply wires sup_warr_list = None sup_pitch = last_sup_track - bus_margin for lay in wire_layers: tr_max = self.grid.find_next_track(lay, bus_upper, mode=1, unit_mode=True) tr_idx_list = list(range(1, tr_max + 1, 2)) avail_list = self.get_available_tracks(lay, tr_idx_list, bus_wl, bus_wu, unit_mode=True) # connect sup_warr_list = [] for aidx in avail_list: sup_warr_list.append( self.add_wires(lay, aidx, bus_wl, bus_wu, unit_mode=True)) self.connect_to_tracks(sup_warr_list, TrackID(bus_layer, bus_margin, width=1, num=2, pitch=sup_pitch), track_lower=0) num_sup_tracks = self.grid.get_num_tracks(self.size, bus_layer + 2) num_top_sup = 1 sup_w = self.grid.get_max_track_width(bus_layer + 2, num_top_sup, num_sup_tracks) # TODO: find best way to do this in process independent way while sup_w > 10: num_top_sup += 1 sup_w = self.grid.get_max_track_width(bus_layer + 2, num_top_sup, num_sup_tracks) sup_tr_list = self.grid.get_evenly_spaced_tracks( num_top_sup, num_sup_tracks, sup_w) for sup_idx in sup_tr_list: sup_warr = self.connect_to_tracks( sup_warr_list, TrackID(bus_layer + 2, sup_idx, width=sup_w)) self.add_pin(sup_name, sup_warr, show=show_pins)
def _get_wire_array(self, layer_id, tr0, num, lower, upper, pitch=1): res = self.config['resolution'] tid = TrackID(layer_id, tr0, num=num, pitch=pitch) return WireArray(tid, lower, upper, res=res, unit_mode=True)
def draw_dum_connection(self, template, mos_info, edge_mode, gate_tracks, options): # type: (TemplateBase, Dict[str, Any], int, List[int], Dict[str, Any]) -> None res = self.config['resolution'] mos_lay_table = self.config['mos_layer_table'] lay_name_table = self.config['layer_name'] layout_info = mos_info['layout_info'] sd_yc = mos_info['sd_yc'] lch_unit = layout_info['lch_unit'] sd_pitch = layout_info['sd_pitch'] fg = layout_info['fg'] b_po_y_list = layout_info['b_po_y_list'] g_y_list = layout_info['g_y_list'] d_y_list = layout_info['d_y_list'] b_y_list = layout_info['b_y_list'] mos_constants = self.get_mos_tech_constants(lch_unit) sp_gb_po = mos_constants['sp_gb_po'] po_od_spx = mos_constants['po_od_spx'] w_delta = mos_constants['w_delta'] sd_pitch2 = sd_pitch // 2 width = fg * sd_pitch lch2 = lch_unit // 2 po_name = mos_lay_table['PO_dummy'] od_name = mos_lay_table['OD_dummy'] po_yb = g_y_list[0][0] - sd_yc po_yt = b_po_y_list[0][0] - sp_gb_po - sd_yc od_yb = d_y_list[0][0] - sd_yc od_yt = d_y_list[0][1] + w_delta - sd_yc od_w = sd_pitch - lch_unit - 2 * po_od_spx if (edge_mode & 1) == 0: po_box = BBox(sd_pitch2 - lch2, po_yb, sd_pitch2 + lch2, po_yt, res, unit_mode=True) template.add_rect(po_name, po_box, unit_mode=True) po_xcl = sd_pitch2 * 3 else: po_xcl = sd_pitch2 od_xl = sd_pitch2 + lch2 + po_od_spx od_xr = od_xl + od_w if (edge_mode & 2) == 0: po_box = BBox(width - sd_pitch2 - lch2, po_yb, width - sd_pitch2 + lch2, po_yt, res, unit_mode=True) template.add_rect(po_name, po_box, unit_mode=True) po_xcr = width - sd_pitch2 * 3 else: po_xcr = width - sd_pitch2 # get dummy PO X coordinates nx = (po_xcr - po_xcl) // sd_pitch + 1 po_yt = b_po_y_list[0][1] - sd_yc if nx > 0: # draw dummy PO po_box = BBox(po_xcl - lch2, po_yb, po_xcl + lch2, po_yt, res, unit_mode=True) template.add_rect(po_name, po_box, nx=nx, spx=sd_pitch, unit_mode=True) if fg > 1: od_box = BBox(od_xl, od_yb, od_xr, od_yt, res, unit_mode=True) template.add_rect(od_name, od_box, nx=fg - 1, spx=sd_pitch, unit_mode=True) # draw body M1 m1_name = lay_name_table[1] m1_yb, m1_yt = b_y_list[1] m1_box = BBox(0, m1_yb, width, m1_yt, res, unit_mode=True) m1_box = m1_box.move_by(dy=-sd_yc, unit_mode=True) template.add_rect(m1_name, m1_box) # add body ports dum_layer = self.get_dum_conn_layer() for tidx in gate_tracks: warr = WireArray(TrackID(dum_layer, tidx), m1_box.bottom_unit, m1_box.top_unit, res=res, unit_mode=True) template.add_pin('dummy', warr, show=False)
def draw_res_core(self, template, layout_info): # type: (TemplateBase, Dict[str, Any]) -> None mos_lay_table = self.config['mos_layer_table'] res_lay_table = self.config['res_layer_table'] fin_h2 = self.mos_tech.mos_config['fin_h'] // 2 fin_p2 = self.mos_tech.mos_config['mos_pitch'] // 2 grid = template.grid res = grid.resolution finfet_lay = mos_lay_table['FB'] rpdmy_lay = res_lay_table['RPDMY'] res_lay = res_lay_table['RES'] w = layout_info['w'] l = layout_info['l'] threshold = layout_info['threshold'] res_type = layout_info['res_type'] sub_type = layout_info['sub_type'] wcore = layout_info['w_core'] hcore = layout_info['h_core'] track_widths = layout_info['track_widths'] core_info = layout_info['core_info'] lr_od_loc = core_info['lr_od_loc'] top_od_loc = core_info['top_od_loc'] bot_od_loc = core_info['bot_od_loc'] lr_fg = core_info['lr_fg'] tb_fg = core_info['tb_fg'] port_info = core_info['port_info'] fill_info = core_info['fill_info'] xc, yc = wcore // 2, hcore // 2 wres, lres, _, _ = self.get_res_dimension(l, w) # set size and draw implant layers implant_layers = self.get_res_imp_layers(res_type, sub_type, threshold) arr_box = BBox(0, 0, wcore, hcore, res, unit_mode=True) # mos layer need to be snap to fin edges. fin_box = BBox(0, -fin_p2 - fin_h2, wcore, hcore + fin_p2 + fin_h2, res, unit_mode=True) for lay in implant_layers: if lay == finfet_lay: template.add_rect(lay, fin_box) else: template.add_rect(lay, arr_box) template.array_box = arr_box template.prim_bound_box = arr_box template.add_cell_boundary(arr_box) # draw RPDMY rpdmy_yb = yc - l // 2 rpdmy_yt = rpdmy_yb + l template.add_rect( rpdmy_lay, BBox(0, rpdmy_yb, wcore, rpdmy_yt, res, unit_mode=True)) # draw resistor rh_yb = yc - lres // 2 rh_xl = xc - wres // 2 template.add_rect( res_lay, BBox(rh_xl, rh_yb, rh_xl + wres, rh_yb + lres, res, unit_mode=True)) # draw vias and ports bot_layer = self.get_bot_layer() for port_name, rect_list, via_list in port_info: for rect_info in rect_list: template.add_rect(rect_info['layer'], rect_info['bbox']) for via_params in via_list: template.add_via_primitive(**via_params) tr_bbox = rect_list[-1]['bbox'] port_tr = grid.coord_to_track(bot_layer, tr_bbox.yc_unit, unit_mode=True) pin_warr = WireArray(TrackID(bot_layer, port_tr, width=track_widths[0]), tr_bbox.left_unit, tr_bbox.right_unit, res=res, unit_mode=True) template.add_pin(port_name, pin_warr, show=False) # draw dummies self._draw_dummies(template, 0, lr_fg, lr_od_loc) self._draw_dummies(template, wcore, lr_fg, lr_od_loc) self._draw_dummies(template, xc, tb_fg, top_od_loc) self._draw_dummies(template, xc, tb_fg, bot_od_loc) # draw metal fill for layer, exc_layer, _, _, core_x, core_y, _, _, _, _ in fill_info: template.add_rect(exc_layer, arr_box) for xl, xr in core_x: for yb, yt in core_y: template.add_rect( layer, BBox(xl, yb, xr, yt, res, unit_mode=True))
def draw_res_core(self, template, layout_info): # type: (TemplateBase, Dict[str, Any]) -> 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] 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'] wcore = layout_info['w_core'] hcore = layout_info['h_core'] track_widths = layout_info['track_widths'] core_info = layout_info['core_info'] lr_od_xloc = core_info['lr_od_xloc'] top_od_yloc = core_info['top_od_yloc'] bot_od_yloc = core_info['bot_od_yloc'] tb_od_xloc = core_info['tb_od_xloc'] port_info = core_info['port_info'] res_info = res_info[res_type] need_rpo = res_info['need_rpo'] need_rpdmy = res_info['need_rpdmy'] od_in_res = res_info['od_in_res'] xc, yc = wcore // 2, hcore // 2 wres, lres, _, _ = self.get_res_dimension(l, w) # set size and draw implant layers implant_layers = self.get_res_imp_layers(res_type, sub_type, threshold) arr_box = BBox(0, 0, wcore, hcore, res, unit_mode=True) po_yb = yc - lres // 2 po_yt = yc + lres // 2 if od_in_res: imp_box = arr_box else: imp_box = BBox(0, po_yb - imp_ency, wcore, po_yt + imp_ency, res, unit_mode=True) for lay in implant_layers: template.add_rect(lay, imp_box) template.array_box = arr_box template.prim_bound_box = arr_box # draw RPDMY po_xl = xc - wres // 2 po_xr = xc + wres // 2 rpdmy_yb = yc - l // 2 rpdmy_yt = rpdmy_yb + l if need_rpdmy: rpdmy_name = res_layer_table['RPDMY'] template.add_rect( rpdmy_name, BBox(po_xl, rpdmy_yb, po_xr, rpdmy_yt, res, unit_mode=True)) if need_rpo: # draw RPO rpo_name = res_layer_table['RPO'] template.add_rect( rpo_name, BBox(0, rpdmy_yb, wcore, rpdmy_yt, res, unit_mode=True)) # draw PO po_name = mos_layer_table['PO'] template.add_rect( po_name, BBox(po_xl, po_yb, po_xr, po_yt, res, unit_mode=True)) # draw vias and ports m1_name = layer_table[1] bot_layer = self.get_bot_layer() for port_name, v0_params, v1_params, m1_box, m2_box in port_info: template.add_rect(m1_name, m1_box) template.add_via_primitive(**v0_params) template.add_via_primitive(**v1_params) m2_tr = grid.coord_to_track(bot_layer, m2_box.yc_unit, unit_mode=True) pin_warr = WireArray(TrackID(bot_layer, m2_tr, width=track_widths[0]), m2_box.left_unit, m2_box.right_unit, res=res, unit_mode=True) template.add_pin(port_name, pin_warr, show=False) # draw dummies po_y_list = [(po_yb, po_yt)] self._draw_dummies(template, lr_od_xloc, po_y_list) self._draw_dummies(template, lr_od_xloc, po_y_list, dx=wcore) self._draw_dummies(template, tb_od_xloc, bot_od_yloc) self._draw_dummies(template, tb_od_xloc, top_od_yloc) # draw M1 exclusion layer m1_exc_layer = metal_exclude_table[1] template.add_rect(m1_exc_layer, arr_box) # draw M1 fill m1_x_list = core_info['m1_core_x'] m1_y_list = core_info['m1_core_y'] for xl, xr in m1_x_list: for yb, yt in m1_y_list: template.add_rect(m1_name, BBox(xl, yb, xr, yt, res, unit_mode=True))