def _do_dummy_fill(self, top_layer, tapx, tap1, offset, offlev, samp): res = self.grid.resolution tapx_box = tapx.fill_box off_box = offset.fill_box tap1_box = tap1.fill_box lev_box = offlev.fill_box samp_box = samp.fill_box yb = tapx_box.bottom_unit yt = tapx_box.top_unit box1 = BBox(tapx_box.right_unit, yb, off_box.left_unit, yt, res, unit_mode=True) box2 = BBox(tap1_box.right_unit, yb, lev_box.left_unit, yt, res, unit_mode=True) params = dict( mos_type='nch', threshold='standard', width=box1.width_unit, height=box1.height_unit, ) dum1 = self.new_template(params=params, temp_cls=DummyFillActive) params['width'] = box2.width_unit params['height'] = box2.height_unit dum2 = self.new_template(params=params, temp_cls=DummyFillActive) self.add_instance(dum1, 'XDUM1', loc=(box1.left_unit, box1.bottom_unit), unit_mode=True) self.add_instance(dum2, 'XDUM2', loc=(box2.left_unit, box2.bottom_unit), unit_mode=True) hm_layer = top_layer - 1 for layer in range(1, hm_layer): self.do_max_space_fill(layer, bound_box=box1) self.do_max_space_fill(layer, bound_box=box2) self.fill_box = tapx_box.merge(samp_box) self.do_max_space_fill(hm_layer, self.fill_box, fill_pitch=2)
def _draw_g_via(self, template, lch_unit, m1_yb, m1_yt, x_list, r_list, l_list): mos_lay_table = self.config['mos_layer_table'] via_table = self.config['via_id'] layer_table = self.config['layer_name'] mos_constants = self.get_mos_tech_constants(lch_unit) sd_pitch = mos_constants['sd_pitch'] g_via_info = mos_constants['laygo_g_via'] g_v0_w, g_v0_h = g_via_info['dim'][0] g_m1_h = g_v0_h + 2 * g_via_info['top_enc_le'][0] g_m1_yb = m1_yt - g_m1_h g_m1_yc = (g_m1_yb + m1_yt) // 2 bot_encx = g_via_info['bot_enc_side'][0] bot_ency = g_via_info['bot_enc_le'][0] top_encx = g_via_info['top_enc_side'][0] top_ency = g_via_info['top_enc_le'][0] enc1 = [bot_encx, bot_encx, bot_ency, bot_ency] enc2 = [top_encx, top_encx, top_ency, top_ency] conn_layer = self.get_dig_conn_layer() via_id = via_table[(mos_lay_table['PO'], layer_table[conn_layer])] for xc, rflag, lflag in zip(x_list, r_list, l_list): if rflag: template.add_via_primitive(via_id, [xc + sd_pitch // 2, g_m1_yc], enc1=enc1, enc2=enc2, unit_mode=True) template.add_rect( ('M1', 'drawing'), BBox(xc, g_m1_yb, xc + sd_pitch // 2 + g_v0_w // 2 + top_encx, m1_yt, self.res, unit_mode=True)) if lflag: template.add_via_primitive(via_id, [xc - sd_pitch // 2, g_m1_yc], enc1=enc1, enc2=enc2, unit_mode=True) template.add_rect( ('M1', 'drawing'), BBox(xc - sd_pitch // 2 - g_v0_w // 2 - top_encx, g_m1_yb, xc, m1_yt, self.res, unit_mode=True))
def _draw_dummies(self, template, xintv_list, yintv_list, dx=0, dy=0): mos_layer_table = self.config['mos_layer_table'] od_dummy_lay = mos_layer_table['OD_dummy'] res = template.grid.resolution for xl, xr in xintv_list: for yb, yt in yintv_list: box = BBox(xl, yb, xr, yt, res, unit_mode=True) template.add_rect(od_dummy_lay, box.move_by(dx=dx, dy=dy, unit_mode=True))
def _draw_dummies(self, template, xc, fg, od_po_loc): mos_lay_table = self.config['mos_layer_table'] po_lch = self.res_config['po_lch'] po_pitch = self.res_config['po_pitch'] po_od_exty = self.res_config['po_od_exty'] mp_spy_dum = self.res_config['mp_spy_dum'] mp_h_dum = self.res_config['mp_h_dum'] od_mp_ency_dum = self.res_config['od_mp_ency_dum'] po_h_min = self.res_config['po_h_min'] mp_pitch = mp_spy_dum + mp_h_dum res = template.grid.resolution od_w = po_lch + (fg - 1) * po_pitch od_xl = xc - od_w // 2 od_xr = od_xl + od_w po_xr = od_xl + po_lch od_loc, po_loc = od_po_loc od_dum_lay = mos_lay_table['OD_dummy'] po_dum_lay = mos_lay_table['PO_dummy'] mp_dum_lay = mos_lay_table['MP_dummy'] if od_loc: # draw dummy transistors for od_yb, od_yt in od_loc: po_yb = od_yb - po_od_exty po_yt = od_yt + po_od_exty # draw OD template.add_rect(od_dum_lay, BBox(od_xl, od_yb, od_xr, od_yt, res, unit_mode=True)) # draw PO template.add_rect(po_dum_lay, BBox(od_xl, po_yb, po_xr, po_yt, res, unit_mode=True), nx=fg, spx=po_pitch * res) # draw M0PO # compute number of M0PO od_h = od_yt - od_yb avail_sp = od_h - 2 * od_mp_ency_dum num_mp = (avail_sp - mp_h_dum) // mp_pitch + 1 if num_mp > 0: mp_harr = mp_h_dum + (num_mp - 1) * mp_pitch mp_xl = od_xl + po_lch // 2 mp_xr = od_xr - po_lch // 2 mp_yb = od_yb + (od_h - mp_harr) // 2 mp_yt = mp_yb + mp_h_dum template.add_rect(mp_dum_lay, BBox(mp_xl, mp_yb, mp_xr, mp_yt, res, unit_mode=True), ny=num_mp, spy=mp_pitch * res) elif po_loc is not None: # draw dummy PO only po_yb, po_yt = po_loc if po_yt - po_yb >= po_h_min: template.add_rect(po_dum_lay, BBox(od_xl, po_yb, po_xr, po_yt, res, unit_mode=True), nx=fg, spx=po_pitch * res)
def intersection_iter(self, box, dx=0, dy=0): # type: (BBox, int, int) -> Generator[BBox, None, None] """Finds all bounding box that intersects the given box.""" res = self._res test_box = box.expand(dx=dx, dy=dy, unit_mode=True) box_iter = self._index.intersection( test_box.get_bounds(unit_mode=True), objects='raw') for xl, yb, xr, yt, sdx, sdy in box_iter: box_real = BBox(xl, yb, xr, yt, res, unit_mode=True) box_sp = box_real.expand(dx=sdx, dy=sdy, unit_mode=True) if box_sp.overlaps(box) or test_box.overlaps(box_real): yield box_real.expand(dx=max(dx, sdx), dy=max(dy, sdy), unit_mode=True)
def draw_mos(self, template, layout_info): # type: (TemplateBase, Dict[str, Any]) -> None """Draw transistor geometries. Note: because body-connected transistors in SOI technology cannot be drawn on the same row, this method only draws implant and threshold layers. The actual transistor is drawn in draw_mos_connection() method. For substrate row, this method just draws a large M1 rectangle to short all body connections together. """ res = self.config['resolution'] sd_pitch = layout_info['sd_pitch'] fg = layout_info['fg'] arr_yb, arr_yt = layout_info['arr_y'] imp_info = layout_info['imp_info'] blk_type = layout_info['blk_type'] # compute array/bounding box size width = fg * sd_pitch template.array_box = BBox(0, arr_yb, width, arr_yt, res, unit_mode=True) template.prim_bound_box = template.array_box template.prim_top_layer = self.get_mos_conn_layer() # draw implant layers if width > 0: for imp_name, (imp_yb, imp_yt) in imp_info.items(): imp_box = BBox(0, imp_yb, width, imp_yt, res, unit_mode=True) template.add_rect(imp_name, imp_box) if blk_type == 'sub': # draw M1 rectangle for substrate contact m1_extx = layout_info['m1_extx'] mx_yb, mx_yt = layout_info['mx_y'] sub_box = BBox(-m1_extx, mx_yb, width + m1_extx, mx_yt, res, unit_mode=True) m1_name = self.config['layer_name'][1] template.add_rect(m1_name, sub_box)
def draw_layout(self): # type: () -> None res = self.grid.resolution edge_margin = int(round(self.params['cap_edge_margin'] / res)) cap_height = int(round(self.params['cap_height'] / res)) num_cap_layer = self.params['num_cap_layer'] show_pins = self.params['show_pins'] # place instances io_layer, io_width, cap_yt, resout = self.place() # draw AC coupling caps bounding boxes # figure out cap left/right X coordinate blk_w = self.grid.get_size_dimension(self.size, unit_mode=True)[0] xl = edge_margin xr = blk_w - edge_margin # draw mom caps and get cap ports cap_yt = min(cap_yt, edge_margin + cap_height) cap_box = BBox(xl, edge_margin, xr, cap_yt, res, unit_mode=True) # make sure both left and right ports on vertical layers are in port_parity = {lay: (0, 1) for lay in range(io_layer, io_layer + num_cap_layer, 2)} for lay in range(io_layer + 1, io_layer + num_cap_layer, 2): port_parity[lay] = (1, 1) cap_ports = self.add_mom_cap(cap_box, io_layer, num_cap_layer, port_widths=io_width, port_parity=port_parity) out_layer = io_layer + num_cap_layer - 1 self.connect_to_tracks(resout, cap_ports[io_layer][0][0].track_id) self.add_pin('out', cap_ports[out_layer][0], show=show_pins) self.add_pin('in', cap_ports[out_layer][1], show=show_pins)
def _fill_dummy(self, top_layer, fill_top_layer, tot_box, inst_res, inst_cap, dum_params): res = self.grid.resolution box_res = inst_res.bound_box box_cap = inst_cap.bound_box # fill empty space yt = tot_box.top_unit for box in (box_res, box_cap): if box.top_unit < yt: cur_box = BBox(box.left_unit, box.top_unit, box.right_unit, yt, res, unit_mode=True) self._fill_space(cur_box, dum_params, fill_top_layer) # fill resistor for lay in range(inst_res.master.top_layer, fill_top_layer + 1): self.do_max_space_fill(lay, box_res, fill_pitch=2) # fill capacitor for lay in range(inst_cap.master.top_layer, top_layer): self.do_max_space_fill(lay, box_cap, fill_pitch=2)
def draw_layout(self): # type: () -> None fill_config = self.params['fill_config'] bot_layer = self.params['bot_layer'] top_layer = self.params['top_layer'] show_pins = self.params['show_pins'] if top_layer is None: top_layer = bot_layer + 1 blk_w, blk_h = self.grid.get_fill_size(top_layer, fill_config, unit_mode=True) bnd_box = BBox(0, 0, blk_w, blk_h, self.grid.resolution, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.array_box = bnd_box vdd_list, vss_list = None, None for lay in range(bot_layer, top_layer + 1): fill_width, fill_space, space, space_le = fill_config[lay] vdd_list, vss_list = self.do_power_fill(lay, space, space_le, vdd_warrs=vdd_list, vss_warrs=vss_list, fill_width=fill_width, fill_space=fill_space, unit_mode=True) if lay == bot_layer: self.add_pin('VDD_b', vdd_list, show=False) self.add_pin('VSS_b', vss_list, show=False) self.add_pin('VDD', vdd_list, show=show_pins) self.add_pin('VSS', vss_list, show=show_pins)
def draw_bridges(self, x0, y0, x1, y1, w_gnd, w_bridge, sep_bridge, gap, layer_id, bridge_layer_id): res = self.grid.resolution dir_vec = np.array((x1 - x0, y1 - y0), dtype=int) seg_length = np.max(np.abs(dir_vec)) # type: int dir_unit = dir_vec // seg_length ortho_unit = np.array((-dir_unit[1], dir_unit[0]), dtype=int) if x1 == x0: # vertical wire wire_dir = 'y' box = BBox(-w_gnd // 2, -w_bridge // 2, w_gnd // 2, w_bridge // 2, res, unit_mode=True) else: wire_dir = 'x' box = BBox(-w_bridge // 2, -w_gnd // 2, w_bridge // 2, w_gnd // 2, res, unit_mode=True) num_seg = seg_length // sep_bridge if num_seg > 0: values = np.linspace(0.0, seg_length, num_seg + 2, endpoint=True) bridge_layer = self.grid.tech_info.get_layer_name(bridge_layer_id) orig = np.array((x0, y0), dtype=int) for sel in values[1:-1]: sel = int(round(sel)) mp0 = orig + dir_unit * sel - ortho_unit * gap box0 = box.move_by(dx=mp0[0], dy=mp0[1], unit_mode=True) delta = ortho_unit * 2 * gap box1 = box0.move_by(dx=delta[0], dy=delta[1], unit_mode=True) wbox = box0.merge(box1) self.add_rect(bridge_layer, wbox) for idx in range(bridge_layer_id, layer_id): bot_layer = self.grid.tech_info.get_layer_name(idx) top_layer = self.grid.tech_info.get_layer_name(idx + 1) bot_via_dir = self.grid.get_direction(idx) self.add_via(box0, bot_layer, top_layer, bot_via_dir) self.add_via(box1, bot_layer, top_layer, bot_via_dir)
def intersection_rect_iter(self, box): # type: (BBox) -> Generator[BBox, None, None] """Finds all bounding box that intersects the given box.""" res = self._res box_iter = self._index.intersection(box.get_bounds(unit_mode=True), objects='raw') for xl, yb, xr, yt, sdx, sdy in box_iter: yield BBox(xl, yb, xr, yt, res, unit_mode=True)
def bound_box(self): # type: () -> BBox xl, yb, xr, yt = self._index.bounds return BBox(int(xl), int(yb), int(xr), int(yt), self._res, unit_mode=True)
def draw_layout(self): res = self.grid.resolution core_params = self.params.copy() tot_width = core_params.pop('tot_width') show_pins = core_params['show_pins'] core_params['show_pins'] = False core_master = self.new_template(params=core_params, temp_cls=NPassGateWClkCore) sd_pitch = core_master.layout_info.sd_pitch_unit tot_width *= sd_pitch cur_width = core_master.layout_info.get_total_width(core_master.fg_tot) if cur_width > tot_width: raise ValueError( 'Need at least width=%d, but constrained to have width=%d' % (cur_width, tot_width)) xshift = (tot_width - cur_width) // 2 inst = self.add_instance(core_master, loc=(xshift, 0), unit_mode=True) vdd_warrs = inst.get_all_port_pins('VDD') vss_warrs = inst.get_all_port_pins('VSS') # set size vdd_lay_id = vdd_warrs[0].layer_id top_layer = vdd_lay_id + 2 height = inst.bound_box.height_unit self.set_size_from_bound_box( top_layer, BBox(0, 0, tot_width, height, res, unit_mode=True)) # fill and export supplies sup_width = 2 fill_margin = 0.5 edge_margin = 0.2 lay_id = vdd_lay_id + 1 vdd_warrs, vss_warrs = self.do_power_fill(lay_id, vdd_warrs, vss_warrs, sup_width=sup_width, fill_margin=fill_margin, edge_margin=edge_margin) lay_id += 1 edge_margin = 0 vdd_warrs, vss_warrs = self.do_power_fill(lay_id, vdd_warrs, vss_warrs, sup_width=sup_width, fill_margin=fill_margin, edge_margin=edge_margin) self.add_pin('VSS', vss_warrs, show=show_pins) self.add_pin('VDD', vdd_warrs, show=show_pins) for port_name in inst.port_names_iter(): if port_name != 'VSS' and port_name != 'VDD': self.reexport(inst.get_port(port_name), show=show_pins)
def draw_layout(self): """ Specifies the creation of the lumerical shapes """ self.add_rect(layer='SI', bbox=BBox(left=0, bottom=0, right=self.params['width'], top=self.params['length'], resolution=self.grid.resolution, unit_mode=False), unit_mode=False)
def draw_layout(self): # type: () -> None fill_config = self.params['fill_config'] decap_params = self.params['decap_params'] nx = self.params['nx'] ny = self.params['ny'] top_layer = self.params['top_layer'] show_pins = self.params['show_pins'] params = decap_params.copy() params['nx'] = nx params['ny'] = ny params['fill_config'] = fill_config params['top_layer'] = top_layer master_cap = self.new_template(params=params, temp_cls=DecapFillCore) w_blk, h_blk = self.grid.get_fill_size(top_layer, fill_config, unit_mode=True) w_tot = w_blk * nx h_tot = h_blk * ny dx = (w_tot - master_cap.bound_box.width_unit) // 2 cap_inst = self.add_instance(master_cap, 'XCAP', (dx, 0), unit_mode=True) hm_layer = master_cap.mos_conn_layer + 1 if top_layer <= hm_layer: raise ValueError('top layer must be at least %d' % (hm_layer + 1)) # set size res = self.grid.resolution self.array_box = bnd_box = BBox(0, 0, w_tot, h_tot, res, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.add_cell_boundary(bnd_box) # do power fill ym_layer = hm_layer + 1 vdd_list = cap_inst.get_all_port_pins('VDD') vss_list = cap_inst.get_all_port_pins('VSS') fill_width, fill_space, space, space_le = fill_config[ym_layer] vdd_list, vss_list = self.do_power_fill(ym_layer, space, space_le, vdd_warrs=vdd_list, vss_warrs=vss_list, fill_width=fill_width, fill_space=fill_space, unit_mode=True) if top_layer > ym_layer: params = dict(fill_config=fill_config, show_pins=False) inst = None for bot_layer in range(ym_layer, top_layer): params['bot_layer'] = bot_layer master = self.new_template(params=params, temp_cls=PowerFill) inst = self.add_instance(master, 'X%d' % bot_layer, nx=nx, ny=ny, spx=w_blk, spy=h_blk, unit_mode=True) vdd_list = self.connect_wires(inst.get_all_port_pins('VDD')) vss_list = self.connect_wires(inst.get_all_port_pins('VSS')) self.add_pin('VDD', vdd_list, show=show_pins) self.add_pin('VSS', vss_list, show=show_pins)
def get_substrate_box(self): # type: () -> Tuple[Optional[BBox], Optional[BBox]] """Returns the substrate tap bounding box.""" (imp_yb, imp_yt), (thres_yb, thres_yt) = self._sub_bndy xl, xr = self._sub_bndx if xl is None or xr is None: return None, None res = self.grid.resolution if imp_yb is None or imp_yt is None: imp_box = None else: imp_box = BBox(xl, imp_yb, xr, imp_yt, res, unit_mode=True) if thres_yb is None or thres_yt is None: thres_box = None else: thres_box = BBox(xl, thres_yb, xr, thres_yt, res, unit_mode=True) return imp_box, thres_box
def draw_layout(self): mos_type = self.params['mos_type'] threshold = self.params['threshold'] w = self.params['width'] h = self.params['height'] # draw fill tech_cls = self.grid.tech_info.tech_params['layout']['mos_tech_class'] tech_cls.draw_active_fill(self, mos_type, threshold, w, h) # set size box = BBox(0, 0, w, h, self.grid.resolution, unit_mode=True) self.prim_top_layer = 1 self.array_box = self.prim_bound_box = box self.grid.tech_info.draw_device_blockage(self)
def _draw_layout_helper(self, cap_bot_layer, cap_top_layer, cap_width, cap_height, sub_lch, sub_w, sub_type, threshold, show_pins): res = self.grid.resolution cap_width = int(round(cap_width / res)) cap_height = int(round(cap_height / res)) blk_w, _ = self.grid.get_block_size(cap_top_layer, unit_mode=True) w_pitch, _ = self.grid.get_size_pitch(cap_top_layer, unit_mode=True) tot_width = -(-cap_width // blk_w) * blk_w sub_params = dict( lch=sub_lch, w=sub_w, sub_type=sub_type, threshold=threshold, top_layer=cap_top_layer, blk_width=tot_width // w_pitch, show_pins=False, ) sub_master = self.new_template(params=sub_params, temp_cls=SubstrateContact) inst = self.add_instance(sub_master, inst_name='XSUB') port_name = 'VDD' if sub_type == 'ntap' else 'VSS' self.reexport(inst.get_port(port_name), show=show_pins) subw, subh = self.grid.get_size_dimension(sub_master.size, unit_mode=True) self.size = self.grid.get_size_tuple(cap_top_layer, tot_width, subh + cap_height, round_up=True, unit_mode=True) self.array_box = self.bound_box cap_xl = self.array_box.xc_unit - cap_width // 2 cap_box = BBox(cap_xl, subh, cap_xl + cap_width, subh + cap_height, res, unit_mode=True) cap_ports = self.add_mom_cap(cap_box, cap_bot_layer, cap_top_layer - cap_bot_layer + 1, 2) cp, cn = cap_ports[cap_top_layer] self.add_pin('plus', cp, show=show_pins) self.add_pin('minus', cn, show=show_pins)
def draw_layout(self): """ Specifies the creation of the lumerical shapes """ self.add_rect(layer='SI', bbox=BBox(left=0, bottom=0, right=self.params['width'], top=self.params['length'], resolution=self.grid.resolution, unit_mode=False), unit_mode=False) sub_master = self.new_template(params={ 'length': self.params['width'], 'width': self.params['length'] }, temp_cls=AddSubRect) self.add_instance(master=sub_master)
def _draw_mom_cap(self, cap_x_list, bot_layer, top_layer, cap_spy, cap_h_list, port_tr_w, show_pins): grid = self.grid res = grid.resolution # get port location bnd_box = self.bound_box cap_yt = bnd_box.top_unit - cap_spy # draw MOM cap num_layer = top_layer - bot_layer + 1 out_list = [] out_res_info = in_res_info = None for cap_idx, ((cap_xl, cap_xr), cap_h) in enumerate(zip(cap_x_list, cap_h_list)): cap_yb = max(bnd_box.bottom_unit + cap_spy, cap_yt - cap_h) cap_box = BBox(cap_xl, cap_yb, cap_xr, cap_yt, res, unit_mode=True) parity = cap_idx % 2 port_par = (parity, 1 - parity) ports = self.add_mom_cap(cap_box, bot_layer, num_layer, port_widths=port_tr_w, port_parity={ bot_layer: port_par, top_layer: port_par }) if parity == 0: warr_in = ports[top_layer][1 - parity][0] out = ports[bot_layer][parity][0] else: warr_in = ports[top_layer][parity][0] out = ports[bot_layer][1 - parity][0] out_list.append(out) # draw output metal resistor and port out_port, out_res_info = self._add_metal_res(out, go_up=True) self.add_pin('out<%d>' % cap_idx, out_port, show=show_pins) # draw clock metal resistor and port in_port, in_res_info = self._add_metal_res(warr_in, go_up=False) self.add_pin('in<%d>' % cap_idx, in_port, show=show_pins) # return ports return out_list, out_res_info, in_res_info
def draw_res_boundary(self, template, boundary_type, layout_info, end_mode): # type: (TemplateBase, str, Dict[str, Any]) -> None wcore, hcore = self.res_config['core_size'] wedge, hedge = self.res_config['edge_size'] template_lib = self.res_config['template_lib'] core_cell = self.res_config['%s_cell' % boundary_type] template.add_instance_primitive(template_lib, core_cell, (0, 0)) if boundary_type == 'corner': wbox, hbox = wedge, hedge elif boundary_type == 'lr': wbox, hbox = wedge, hcore else: wbox, hbox = wcore, hedge res = template.grid.resolution template.prim_bound_box = BBox(0, 0, wbox, hbox, res, unit_mode=True) template.array_box = template.prim_bound_box template.prim_top_layer = self.get_bot_layer()
def draw_res_core(self, template, layout_info): # type: (TemplateBase, Dict[str, Any]) -> None template_lib = self.res_config['template_lib'] core_cell = self.res_config['core_cell'] btr, ttr = self.res_config['port_tracks'] xl, xr = self.res_config['port_coord'] wcore, hcore = self.res_config['core_size'] template.add_instance_primitive(template_lib, core_cell, (0, 0)) port_layer = self.get_bot_layer() warr = template.add_wires(port_layer, btr, xl, xr, unit_mode=True) template.add_pin('bot', warr, show=False) warr = template.add_wires(port_layer, ttr, xl, xr, unit_mode=True) template.add_pin('top', warr, show=False) res = template.grid.resolution template.prim_bound_box = BBox(0, 0, wcore, hcore, res, unit_mode=True) template.array_box = template.prim_bound_box template.prim_top_layer = port_layer
def draw_layout(self): """ Specifies the creation of the lumerical shapes """ self.add_rect( layer='SI', coord1=self.params['point1'], coord2=self.params['point2'], unit_mode=False, ) self.add_rect( layer='SI', bbox=BBox( left=1, bottom=-10, right=10, top=-7, resolution=self.grid.resolution, unit_mode=False ), unit_mode=False )
def bbox_rotate(self, bbox: BBox, angle: float) -> BBox: """ Given a bbox, finds coordinates for new rotated bbox Parameters ---------- bbox : BBox input bbox angle : float angle in radians to rotate the bbox Returns ------- bbox : BBox output bbox """ ll = [bbox.left, bbox.bottom] new_x = math.cos(angle) * ll[0] - math.sin(angle) * ll[1] new_y = math.sin(angle) * ll[0] + math.cos(angle) * ll[1] return BBox(left=new_x, bottom=new_y, right=new_x + self._res, top=new_y + self._res, unit_mode=False, resolution=self._res)
def set_digital_size(self, num_cols=None): if self._dig_size is None: if num_cols is None: num_cols = 0 for intv in self._used_list: num_cols = max(num_cols, intv.get_end()) self._laygo_info.set_num_col(num_cols) self._dig_size = num_cols, self._num_rows top_layer = self._laygo_info.top_layer width = self._laygo_info.tot_width height = self._ytop[1] bound_box = BBox(0, 0, width, height, self.grid.resolution, unit_mode=True) self.set_size_from_bound_box(top_layer, bound_box) if not self._laygo_info.draw_boundaries: self.array_box = bound_box
def get_via0_info(self, xc, yc, wres, resolution): """Compute resistor CO parameters and metal 1 bounding box.""" mos_layer_table = self.config['mos_layer_table'] layer_table = self.config['layer_name'] via_id_table = self.config['via_id'] co_w = self.res_config['co_w'] co_sp = self.res_config['co_sp'] po_co_encx, po_co_ency = self.res_config['po_co_enc'] m1_co_encx, m1_co_ency = self.res_config['m1_co_enc'] num_co = (wres - po_co_encx * 2 + co_sp) // (co_w + co_sp) m1_h = co_w + 2 * m1_co_ency # get via parameters po_name = mos_layer_table['PO'] m1_name = layer_table[1] via_id = via_id_table[(po_name, m1_name)] via_params = dict( via_type=via_id, loc=[xc, yc], num_cols=num_co, sp_cols=co_sp, enc1=[po_co_encx, po_co_encx, po_co_ency, po_co_ency], enc2=[m1_co_encx, m1_co_encx, m1_co_ency, m1_co_ency], unit_mode=True) varr_w = num_co * (co_w + co_sp) - co_sp m1_type = self.tech_info.get_layer_type(layer_table[1]) m1_min_len = self.tech_info.get_min_length_unit(m1_type, m1_h) m1_w = max(2 * m1_co_encx + varr_w, m1_min_len) m1_xl = xc - m1_w // 2 m1_xr = m1_xl + m1_w m1_yb = yc - m1_h // 2 m1_yt = m1_yb + m1_h m1_box = BBox(m1_xl, m1_yb, m1_xr, m1_yt, resolution, unit_mode=True) return via_params, m1_box
def _place(self): rxclk_params = self.params['rxclk_params'].copy() core_params = self.params['core_params'].copy() ctle_params = self.params['ctle_params'].copy() dlev_cap_params = self.params['dlev_cap_params'].copy() bus_margin = self.params['bus_margin'] show_pins = self.params['show_pins'] # create template masters rxclk_params['parity'] = 0 rxclk_params['show_pins'] = False clk_master0 = self.new_template(params=rxclk_params, temp_cls=RXClkArray) rxclk_params['parity'] = 1 clk_master1 = self.new_template(params=rxclk_params, temp_cls=RXClkArray) core_params['show_pins'] = False core_master = self.new_template(params=core_params, temp_cls=RXCore) in_xm_offset = core_master.in_offset ctle_params['cap_port_offset'] = in_xm_offset ctle_params['show_pins'] = False ctle_master = self.new_template(params=ctle_params, temp_cls=CTLE) dlev_cap_params['show_pins'] = False dlev_cap_params['io_width'] = core_params['hm_cur_width'] dlev_cap_params['io_space'] = core_params['diff_space'] dcap_master = self.new_template(params=dlev_cap_params, temp_cls=DLevCap) clkw, clkh = self.grid.get_size_dimension(clk_master0.size, unit_mode=True) corew, coreh = self.grid.get_size_dimension(core_master.size, unit_mode=True) ctlew, ctleh = self.grid.get_size_dimension(ctle_master.size, unit_mode=True) # compute X coordinate # TODO: less hard coding vbus_layer = AnalogBase.get_mos_conn_layer(self.grid.tech_info) + 2 ibias_width = 2 bus_ibias_names = ['{}_ibias_nmos_intsum', '{}_ibias_dfe<1>', '{}_ibias_dfe<2>', '{}_ibias_dfe<3>', '{}_ibias_offset'] bus_vss_names = ['bias_nmos_analog', 'bias_nmos_digital', 'bias_nmos_summer', 'bias_nmos_tap1', ] bus_vdd_names = ['bias_pmos_analog', 'bias_pmos_digital', 'bias_pmos_summer', '{}_bias_ffe', '{}_bias_dlevp', '{}_bias_dlevn', ] bus_en_names = ['{}_en_dfe1', '{}_offp', '{}_offn'] num_track_tot = len(bus_ibias_names) + len(bus_vss_names) + \ len(bus_vdd_names) + len(bus_en_names) + (2 + bus_margin * 2) * 4 # TODO: handle cases where vbus_layer's pitch is not multiple of all lower vertical layer's pitch bias_width = self.grid.get_track_pitch(vbus_layer, unit_mode=True) * num_track_tot maxw = max(clkw, corew) if maxw == corew: # add some room blk_w = self.grid.get_block_size(core_master.size[0], unit_mode=True)[0] maxw += -(-400 // blk_w) * blk_w x_clk = bias_width + ctlew + maxw - clkw x_core = bias_width + ctlew + maxw - corew clk_inst0 = self.add_instance(clk_master0, 'XCLK0', loc=(x_clk, clkh), orient='MX', unit_mode=True) core_inst = self.add_instance(core_master, 'XCORE', loc=(x_core, clkh), unit_mode=True) clk_inst1 = self.add_instance(clk_master1, 'XCLK1', loc=(x_clk, clkh + coreh), unit_mode=True) ctle_inst = self.add_instance(ctle_master, 'XCTLE', loc=(bias_width, 0), unit_mode=True) bus_order = [(bus_en_names, 'VDD', 1), (bus_vdd_names, 'VDD', 1), (bus_vss_names, 'VSS', 1), (bus_ibias_names, 'VSS', ibias_width)] vdd_list = [] vss_list = [] self._connect_bias_wires(clk_inst0, core_inst, [core_inst, clk_inst1], clkh, 'odd', bus_order, vdd_list, vss_list, x_core) bus_order = bus_order[::-1] self._connect_bias_wires(clk_inst1, core_inst, [clk_inst1], clk_inst1.location_unit[1], 'even', bus_order, vdd_list, vss_list, x_core) # move ctle to center of rxcore mid = core_inst.location_unit[1] + coreh // 2 ctle_inst.move_by(dy=mid - ctleh // 2, unit_mode=True) dcap_inst1 = self.add_instance(dcap_master, 'XDCAP1', loc=(x_core + corew, 0), orient='MX', unit_mode=True) dcap_inst0 = self.add_instance(dcap_master, 'XDCAP0', loc=(x_core + corew, 0), unit_mode=True) # move dcap inst to right Y location, then connect and export for idx, dinst in enumerate((dcap_inst0, dcap_inst1)): core_outp = core_inst.get_all_port_pins('outp_dlev<%d>' % idx)[0] dcap_outp = dinst.get_all_port_pins('outp')[0] hm_layer = core_outp.layer_id hm_pitch = self.grid.get_track_pitch(hm_layer) delta = core_outp.track_id.base_index - dcap_outp.track_id.base_index dinst.move_by(dy=hm_pitch * delta) for dname, cname in zip(['outp', 'outn', 'inp', 'inn'], ['outp_dlev', 'outn_dlev', 'outp_summer', 'outn_summer']): cname += '<%d>' % idx wlist = core_inst.get_all_port_pins(cname) + dinst.get_all_port_pins(dname) w = self.connect_wires(wlist) if dname.startswith('out'): self.add_pin(cname, w, show=show_pins) # compute size xr = dcap_inst0.array_box.right_unit yt = clk_inst1.array_box.top_unit self.array_box = BBox(0, 0, xr, yt, self.grid.resolution, unit_mode=True) self.set_size_from_array_box(clk_master1.size[0]) return clk_inst0, clk_inst1, core_inst, ctle_inst, vdd_list, vss_list, bias_width
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_boundaries( self, # type: LaygoTech template, # type: TemplateBase laygo_info, # type: LaygoBaseInfo num_col, # type: int yt, # type: int bot_end_master, # type: LaygoEndRow top_end_master, # type: LaygoEndRow edgel_infos, # type: List[Tuple[int, str, Dict[str, Any]]] edger_infos, # type: List[Tuple[int, str, Dict[str, Any]]] ): # type: (...) -> Tuple[BBox, List[WireArray], List[WireArray]] """Draw boundaries for LaygoBase/DigitalBase. Parameters ---------- template : TemplateBase the LaygoBase/DigitalBase object to draw layout in. laygo_info : LaygoBaseInfo the LaygoBaseInfo object. num_col : int number of primitive columns in the template. yt : int the top Y coordinate of the template. Used to determine top end row placement. bot_end_master: LaygoEndRow the bottom LaygoEndRow master. top_end_master : LaygoEndRow the top LaygoEndRow master. edgel_infos: List[Tuple[int, str, Dict[str, Any]]] a list of Y coordinate, orientation, and parameters for left edge blocks. edger_infos: List[Tuple[int, str, Dict[str, Any]]] a list of Y coordinate, orientation, and parameters for right edge blocks. Returns ------- arr_box : BBox the array box. vdd_warrs : List[WireArray] any VDD wires in the edge block due to guard ring. vss_warrs : List[WireArray] any VSS wires in the edge block due to guard ring. """ end_mode = laygo_info.end_mode guard_ring_nf = laygo_info.guard_ring_nf col_width = laygo_info.col_width nx = num_col spx = col_width emargin_l, emargin_r = laygo_info.edge_margins ewidth_l, ewidth_r = laygo_info.edge_widths xoffset = emargin_l + ewidth_l # draw top and bottom end row inst = template.add_instance(bot_end_master, inst_name='XRBOT', loc=(xoffset, 0), nx=nx, spx=spx, unit_mode=True) arr_box = inst.array_box inst = template.add_instance(top_end_master, inst_name='XRBOT', loc=(xoffset, yt), orient='MX', nx=nx, spx=spx, unit_mode=True) arr_box = arr_box.merge(inst.array_box) # draw corners left_end = (end_mode & 4) != 0 right_end = (end_mode & 8) != 0 edge_inst_list = [] xr = laygo_info.tot_width for orient, y, master in (('R0', 0, bot_end_master), ('MX', yt, top_end_master)): for x, is_end, flip_lr in ((emargin_l, left_end, False), (xr - emargin_r, right_end, True)): edge_params = dict( is_end=is_end, guard_ring_nf=guard_ring_nf, adj_blk_info=master.get_left_edge_info(), name_id=master.get_layout_basename(), layout_info=master.get_edge_layout_info(), is_laygo=True, ) edge_master = template.new_template(params=edge_params, temp_cls=AnalogEdge) if flip_lr: eorient = 'MY' if orient == 'R0' else 'R180' else: eorient = orient edge_inst_list.append( template.add_instance(edge_master, orient=eorient, loc=(x, y), unit_mode=True)) # draw edge blocks for y, flip_ud, edge_params in edgel_infos: orient = 'MX' if flip_ud else 'R0' edge_master = template.new_template(params=edge_params, temp_cls=AnalogEdge) edge_inst_list.append( template.add_instance(edge_master, orient=orient, loc=(emargin_l, y), unit_mode=True)) for y, flip_ud, edge_params in edger_infos: orient = 'R180' if flip_ud else 'MY' edge_master = template.new_template(params=edge_params, temp_cls=AnalogEdge) edge_inst_list.append( template.add_instance(edge_master, orient=orient, loc=(xr - emargin_r, y), unit_mode=True)) gr_vss_warrs = [] gr_vdd_warrs = [] conn_layer = self.get_dig_conn_layer() for inst in edge_inst_list: if inst.has_port('VDD'): gr_vdd_warrs.extend( inst.get_all_port_pins('VDD', layer=conn_layer)) elif inst.has_port('VSS'): gr_vss_warrs.extend( inst.get_all_port_pins('VSS', layer=conn_layer)) arr_box = arr_box.merge(inst.array_box) # connect body guard rings together gr_vdd_warrs = template.connect_wires(gr_vdd_warrs) gr_vss_warrs = template.connect_wires(gr_vss_warrs) arr_box_x = laygo_info.get_placement_info(num_col).arr_box_x arr_box = BBox(arr_box_x[0], arr_box.bottom_unit, arr_box_x[1], arr_box.top_unit, arr_box.resolution, unit_mode=True) return arr_box, gr_vdd_warrs, gr_vss_warrs
def draw_layout(self): # type: () -> None bot_layer = self.params['bot_layer'] top_layer = self.params['top_layer'] width = self.params['width'] height = self.params['height'] margin = self.params['margin'] in_tid = self.params['in_tid'] out_tid = self.params['out_tid'] port_tr_w = self.params['port_tr_w'] options = self.params['options'] fill_config = self.params['fill_config'] fill_dummy = self.params['fill_dummy'] fill_pitch = self.params['fill_pitch'] mos_type = self.params['mos_type'] threshold = self.params['threshold'] half_blk_x = self.params['half_blk_x'] half_blk_y = self.params['half_blk_y'] show_pins = self.params['show_pins'] res = self.grid.resolution io_layer = top_layer + 1 w_tot = width + 2 * margin h_tot = height + 2 * margin if fill_config is None: w_blk, h_blk = self.grid.get_block_size(io_layer, unit_mode=True, half_blk_x=half_blk_x, half_blk_y=half_blk_y) else: w_blk, h_blk = self.grid.get_fill_size(io_layer, fill_config, unit_mode=True, half_blk_x=half_blk_x, half_blk_y=half_blk_y) w_tot = -(-w_tot // w_blk) * w_blk h_tot = -(-h_tot // h_blk) * h_blk # set size self.array_box = bnd_box = BBox(0, 0, w_tot, h_tot, res, unit_mode=True) self.set_size_from_bound_box(io_layer, bnd_box) self.add_cell_boundary(bnd_box) # get input/output track location io_horiz = self.grid.get_direction(io_layer) == 'x' mid_coord = bnd_box.yc_unit if io_horiz else bnd_box.xc_unit io_tidx = self.grid.coord_to_nearest_track(io_layer, mid_coord, half_track=True, mode=0, unit_mode=True) if in_tid is None: in_tidx = io_tidx in_tr_w = 2 else: in_tidx, in_tr_w = in_tid if out_tid is None: out_tidx = io_tidx out_tr_w = 2 else: out_tidx, out_tr_w = out_tid in_tid = TrackID(io_layer, in_tidx, width=in_tr_w) out_tid = TrackID(io_layer, out_tidx, width=out_tr_w) # setup capacitor options # get port width dictionary. Make sure we can via up to top_layer + 1 in_w = self.grid.get_track_width(io_layer, in_tr_w, unit_mode=True) out_w = self.grid.get_track_width(io_layer, out_tr_w, unit_mode=True) top_port_tr_w = self.grid.get_min_track_width(top_layer, top_w=max(in_w, out_w), unit_mode=True) top_port_tr_w = max(top_port_tr_w, port_tr_w) port_tr_w_dict = { lay: port_tr_w for lay in range(bot_layer, top_layer + 1) } port_tr_w_dict[top_layer] = top_port_tr_w if options is None: options = dict(port_widths=port_tr_w_dict) else: options = options.copy() options['port_widths'] = port_tr_w_dict # draw cap cap_xl = (bnd_box.width_unit - width) // 2 cap_yb = (bnd_box.height_unit - height) // 2 cap_box = BBox(cap_xl, cap_yb, cap_xl + width, cap_yb + height, res, unit_mode=True) num_layer = top_layer - bot_layer + 1 cap_ports = self.add_mom_cap(cap_box, bot_layer, num_layer, **options) # connect input/output, draw metal resistors cout, cin = cap_ports[top_layer] cin = cin[0] cout = cout[0] in_min_len = self.grid.get_min_length(io_layer, in_tr_w, unit_mode=True) res_upper = cin.track_id.get_bounds(self.grid, unit_mode=True)[0] res_lower = res_upper - in_min_len in_lower = min(0, res_lower - in_min_len) self.connect_to_tracks(cin, in_tid, track_lower=in_lower, unit_mode=True) self.add_res_metal_warr(io_layer, in_tidx, res_lower, res_upper, width=in_tr_w, unit_mode=True) in_warr = self.add_wires(io_layer, in_tidx, in_lower, res_lower, width=in_tr_w, unit_mode=True) out_min_len = self.grid.get_min_length(io_layer, out_tr_w, unit_mode=True) res_lower = cout.track_id.get_bounds(self.grid, unit_mode=True)[1] res_upper = res_lower + out_min_len out_upper = max(w_tot, res_upper + out_min_len) self.connect_to_tracks(cout, out_tid, track_upper=out_upper, unit_mode=True) self.add_res_metal_warr(io_layer, out_tidx, res_lower, res_upper, width=out_tr_w, unit_mode=True) out_warr = self.add_wires(io_layer, out_tidx, res_upper, out_upper, width=out_tr_w, unit_mode=True) self.add_pin('plus', in_warr, show=show_pins) self.add_pin('minus', out_warr, show=show_pins) if fill_dummy: for lay in range(1, io_layer + 1): self.do_max_space_fill(lay, bnd_box, fill_pitch=fill_pitch) dum_params = dict(mos_type=mos_type, threshold=threshold, width=w_tot, height=h_tot) master_dum = self.new_template(params=dum_params, temp_cls=DummyFillActive) self.add_instance(master_dum, unit_mode=True) lay_unit = self.grid.layout_unit self._sch_params = dict( res_in_info=(io_layer, in_w * res * lay_unit, in_min_len * res * lay_unit), res_out_info=(io_layer, out_w * res * lay_unit, out_min_len * res * lay_unit), )