def design(self, nand_params: Param, nor_params: Param, pupd_params: Param, export_pins: bool) -> None: self.instances['XNAND'].design(**nand_params) self.instances['XNOR'].design(**nor_params) self.instances['Xpupd'].design(**pupd_params.copy(append=dict( strong=True))) if export_pins: self.add_pin('nand_pu', TermType.output) self.add_pin('nor_pd', TermType.output)
def design(self, sa_params: Param, sr_params: Param, has_rstlb: bool) -> None: inbuf_test = sr_params.get('inbuf_params', None) if inbuf_test is None: raise ValueError('SR latch must have input buffers.') self.instances['XSA'].design(has_rstb=has_rstlb, **sa_params) self.instances['XSR'].design(has_rstb=has_rstlb, **sr_params) if not has_rstlb: self.remove_pin('rstlb')
def _get_buf_lv_master(self, buf_params: Param, append: Mapping[str, Any]) -> InvChainCore: buf_params = buf_params.copy(append=dict( dual_output=True, vertical_output=True, **append ), remove=['sig_locs']) buf_master = self.new_template(InvChainCore, params=buf_params) vm_layer = self.conn_layer + 2 out_tidx = buf_master.get_port('out').get_pins()[0].track_id.base_index prev_tidx = self.tr_manager.get_next_track(vm_layer, out_tidx, 'sig', 'sig', up=False) buf_master = buf_master.new_template_with(sig_locs=dict(outb=prev_tidx)) return buf_master
def get_fill_info(self, mos_type: str, threshold: str, w: int, h: int, el: Param, eb: Param, er: Param, et: Param) -> LayoutInfo: fin_p: int = self._fill_config['mos_pitch'] fin_h: int = self._fill_config['fin_h'] lch: int = self._fill_config['lch'] po_od_exty: int = self._fill_config['po_od_exty'] po_spy: int = self._fill_config['po_spy'] sd_pitch: int = self._fill_config['sd_pitch'] od_spx: int = self._fill_config['od_spx'] od_density_min: float = self._fill_config['od_density_min'] imp_od_encx: int = self._fill_config['imp_od_encx'] imp_po_ency: int = self._fill_config['imp_po_ency'] fin_p2 = fin_p // 2 fin_h2 = fin_h // 2 od_edge_margin = max(imp_od_encx, od_spx // 2) po_edge_margin = max(imp_po_ency, po_spy // 2) mos_layer_table = self.tech_info.config['mos_lay_table'] po_lp = mos_layer_table['PO_DUMMY'] od_lp = mos_layer_table['OD_DUMMY'] fb_lp = mos_layer_table['FB'] builder = LayoutInfoBuilder() # compute fill X intervals bnd_xl = el.get('delta', 0) bnd_xr = w - er.get('delta', 0) bnd_yb = eb.get('delta', 0) bnd_yt = h - et.get('delta', 0) bbox = BBox(bnd_xl, bnd_yb, bnd_xr, bnd_yt) fill_xl = bnd_xl + od_edge_margin fill_xh = bnd_xr - od_edge_margin fill_yl = bnd_yb + po_edge_margin fill_yh = bnd_yt - po_edge_margin # compute fill X/Y intervals od_x_list, od_x_density = self._get_od_x_list(w, fill_xl, fill_xh, sd_pitch, lch, od_spx) if not od_x_list: return builder.get_info(bbox) od_y_density = od_density_min / od_x_density od_y_list = self._get_od_y_list(h, fill_yl, fill_yh, fin_p, fin_h, od_y_density) if not od_y_list: return builder.get_info(bbox) # draw fills ny = len(od_y_list) for idx, (od_yb, od_yt) in enumerate(od_y_list): po_yb = fill_yl if idx == 0 else od_yb - po_od_exty po_yt = fill_yh if idx == ny - 1 else od_yt + po_od_exty for od_xl, od_xr in od_x_list: builder.add_rect_arr(od_lp, BBox(od_xl, od_yb, od_xr, od_yt)) nx = 1 + (od_xr - od_xl - lch) // sd_pitch builder.add_rect_arr(po_lp, BBox(od_xl, po_yb, od_xl + lch, po_yt), nx=nx, spx=sd_pitch) # draw other layers fin_yb = ( (bnd_yb - fin_p2 + fin_h2) // fin_p) * fin_p + fin_p2 - fin_h2 fin_yt = -(-(bnd_yt - fin_p2 - fin_h2) // fin_p) * fin_p + fin_p2 + fin_h2 for imp_lp in self._thres_imp_well_layers_iter(mos_type, threshold): builder.add_rect_arr(imp_lp, BBox(bnd_xl, bnd_yb, bnd_xr, bnd_yt)) builder.add_rect_arr(fb_lp, BBox(bnd_xl, fin_yb, bnd_xr, fin_yt)) return builder.get_info(bbox)
def design(self, dlycell_params: Param, num_insts: int, num_dum: int, flop: bool, flop_char: bool, output_sr_pins: bool) -> None: """To be overridden by subclasses to design this module. This method should fill in values for all parameters in self.parameters. To design instances of this module, you can call their design() method or any other ways you coded. To modify schematic structure, call: rename_pin() delete_instance() replace_instance_master() reconnect_instance_terminal() restore_instance() array_instance() """ if not flop: self.replace_instance_master('XCELL', 'aib_ams', 'aib_dlycell_no_flop', keep_connections=True) self.replace_instance_master('XDUM', 'aib_ams', 'aib_dlycell_no_flop', keep_connections=True) for name in ['RSTb', 'CLKIN', 'iSI', 'SOOUT', 'iSE']: self.remove_pin(name) self.instances['XCELL'].design(**dlycell_params) if num_insts > 2: if output_sr_pins: conn_list = [('in_p', f'a<{num_insts - 2}:0>,dlyin'), ('bk', f'bk<{num_insts - 1}:0>'), ('ci_p', f'b<{num_insts - 1}:0>'), ('out_p', f'b<{num_insts-2}:0>,dlyout'), ('co_p', f'a<{num_insts - 1}:0>'), ('si', f'so<{num_insts-2}:0>,iSI'), ('so', f'SOOUT,so<{num_insts-2}:0>'), ('srqb', f'srqb<{num_insts - 1}:0>'), ('srq', f'srq<{num_insts - 1}:0>')] else: conn_list = [ ('in_p', f'a<{num_insts - 2}:0>,dlyin'), ('bk', f'bk<{num_insts - 1}:0>'), ('ci_p', f'b{num_insts - 1},b<{num_insts - 2}:0>'), ('out_p', f'b<{num_insts - 2}:0>,dlyout'), ('co_p', f'a{num_insts - 1},a<{num_insts - 2}:0>'), ('si', f'so<{num_insts - 2}:0>,iSI'), ('so', f'SOOUT,so<{num_insts - 2}:0>'), ] if flop_char: conn_list.append(('bk1', f'flop_q<{num_insts - 1}:0>')) self.add_pin(f'flop_q<{num_insts - 1}:0>', TermType.output) self.rename_instance('XCELL', f'XCELL<{num_insts - 1}:0>', conn_list) elif num_insts == 2: conn_list = [ ('in_p', f'a,dlyin'), ('bk', f'bk<{num_insts - 1}:0>'), ('ci_p', f'b{num_insts - 1},b'), ('out_p', 'b,dlyout'), ('co_p', f'a{num_insts - 1},a'), ('si', f'so,iSI'), ('so', f'SOOUT,so'), ] if output_sr_pins: conn_list += [('srqb', f'srqb<1:0>'), ('srq', f'srq<1:0>')] if flop_char: conn_list.append(('bk1', f'flop_q<{num_insts - 1}:0>')) self.add_pin(f'flop_q<{num_insts - 1}:0>', TermType.output) self.rename_instance('XCELL', f'XCELL<{num_insts - 1}:0>', conn_list) elif num_insts == 1: if flop_char: self.reconnect_instance_terminal('XCELL', 'bk1', 'flop_q<0>') self.add_pin('flop_q<0>', TermType.output) if output_sr_pins: self.reconnect_instance_terminal('XCELL', 'srq', 'srq') self.reconnect_instance_terminal('XCELL', 'srqb', 'srqb') else: raise ValueError( f'num_insts={num_insts} should be greater than 0.') if num_dum > 0: dc_core_params = dlycell_params['dc_core_params'].copy( remove=['output_sr_pins']) dum_params = dlycell_params.copy( remove=['flop_char', 'output_sr_pins'], append={ 'is_dum': True, 'dc_core_params': dc_core_params }) self.instances['XDUM'].design(**dum_params) if num_dum > 1: suffix = f'<{num_dum - 1}:0>' conn_list = [ ('out_p', 'NC_out' + suffix), ('co_p', 'NC_co' + suffix), ('so', 'NC_so' + suffix), ] self.rename_instance('XDUM', 'XDUM' + suffix, conn_list) if flop: self.rename_instance('XNC_so', 'XNC_so' + suffix, [('noConn', 'NC_so' + suffix)]) else: self.remove_instance('XNC_so') self.rename_instance('XNC_co', 'XNC_co' + suffix, [('noConn', 'NC_co' + suffix)]) self.rename_instance('XNC_out', 'XNC_out' + suffix, [('noConn', 'NC_out' + suffix)]) else: for inst in ['XDUM', 'XNC_so', 'XNC_co', 'XNC_out']: self.remove_instance(inst) if output_sr_pins: if num_insts == 2: raise ValueError('oops not supported') pin_name_list = [ ('bk', f'bk<{num_insts - 1}:0>'), ('b', f'b<{num_insts - 1}:0>'), ('a', f'a<{num_insts - 1}:0>'), ] if num_insts > 1 else [] self.add_pin(f'srq<{num_insts-1}:0>', TermType.output) self.add_pin(f'srqb<{num_insts-1}:0>', TermType.output) else: pin_name_list = [ ('bk', f'bk<{num_insts - 1}:0>'), ('b', f'b{num_insts - 1}'), ('a', f'a{num_insts - 1}'), ] if num_insts > 1 else [] for old_name, new_name in pin_name_list: self.rename_pin(old_name, new_name)
def test_get_hash(val): """Check that get_hash() works properly. on supported datatypes""" ans = Param.get_hash(val) assert isinstance(ans, int)
def get_mos_tap_info(self, row_info: MOSRowInfo, conn_layer: int, seg: int, options: Param) -> MOSLayInfo: row_type = row_info.row_type guard_ring: bool = options.get('guard_ring', row_info.guard_ring) if guard_ring: sub_type: MOSType = options.get('sub_type', row_type.sub_type) else: sub_type: MOSType = row_type.sub_type lch = self.lch sd_pitch = self.sd_pitch mp_h: int = self.mos_config['mp_h'] mp_po_extx: int = self.mos_config['mp_po_extx'] md_w: int = self.mos_config['md_w'] mos_lay_table = self.tech_info.config['mos_lay_table'] mp_lp = mos_lay_table['MP'] g_info = self.get_conn_info(1, True) d_info = self.get_conn_info(1, False) threshold = row_info.threshold ds_yt = row_info.ds_conn_y[1] mp_yb, mp_yt = row_info['mp_y'] md_yb, md_yt = row_info['md_y'] md_yc = (md_yt + md_yb) // 2 ds_yb = md_yc - (ds_yt - md_yc) fg = seg num_wire = seg + 1 num_po = num_wire + 1 builder = LayoutInfoBuilder() bbox = self._get_mos_active_rect_list(builder, row_info, fg, row_info.sub_width, sub_type) # Connect gate to MP mp_yc = (mp_yb + mp_yt) // 2 mp_delta = (sd_pitch + lch) // 2 + mp_po_extx if num_po & 1: num_vg = num_po // 2 if num_vg & 1: # we have 3 PO left over in the middle num_vg2 = (num_vg - 1) // 2 num_vgm = 2 else: # we have 5 PO left over in the middle num_vg2 = (num_vg - 2) // 2 num_vgm = 4 # draw middle vg vgm_x = bbox.xm - ((num_vgm - 1) * sd_pitch) // 2 mp_xl = vgm_x - mp_delta mp_xr = vgm_x + (num_vgm - 1) * sd_pitch + mp_delta builder.add_rect_arr(mp_lp, BBox(mp_xl, mp_yb, mp_xr, mp_yt)) builder.add_via(g_info.get_via_info('M1_LiPo', vgm_x, mp_yc, mp_h, nx=num_vgm, spx=sd_pitch)) # draw left/right vg if num_vg2 > 0: vg_pitch = 2 * sd_pitch def _add_vg_half(vg_x: int) -> None: xl = vg_x - mp_delta xr = vg_x + mp_delta builder.add_rect_arr(mp_lp, BBox(xl, mp_yb, xr, mp_yt), nx=num_vg2, spx=vg_pitch) builder.add_via(g_info.get_via_info('M1_LiPo', vg_x, mp_yc, mp_h, nx=num_vg2, spx=vg_pitch)) _add_vg_half(0) _add_vg_half((num_wire - 1) * sd_pitch - (num_vg2 - 1) * vg_pitch) else: # even number of PO, can connect pair-wise num_vg = num_po // 2 vg_pitch = 2 * sd_pitch builder.add_rect_arr(mp_lp, BBox(-mp_delta, mp_yb, mp_delta, mp_yt), nx=num_vg, spx=vg_pitch) builder.add_via(g_info.get_via_info('M1_LiPo', 0, mp_yc, mp_h, nx=num_vg, spx=vg_pitch)) # connect drain/source to M1 m1_yc = (md_yb + md_yt) // 2 via_pitch = d_info.via_h + d_info.via_sp vnum_bot = (md_yt - md_yb - d_info.via_bot_enc * 2 + d_info.via_sp) // via_pitch vnum_top = (ds_yt - ds_yb - d_info.via_top_enc * 2 + d_info.via_sp) // via_pitch vnum = min(vnum_top, vnum_bot) builder.add_via(d_info.get_via_info('M1_LiAct', 0, m1_yc, md_w, ortho=False, num=vnum, nx=num_wire, spx=sd_pitch)) edge_info = MOSEdgeInfo(mos_type=sub_type, has_od=True, is_sub=True) be = BlkExtInfo(row_type, threshold, guard_ring, ImmutableList([(fg, sub_type)]), ImmutableSortedDict()) wire_info = (0, num_wire, sd_pitch) return MOSLayInfo(builder.get_info(bbox), edge_info, edge_info, be, be, g_info=wire_info, d_info=wire_info, s_info=wire_info, shorted_ports=ImmutableList())
def get_mos_conn_info(self, row_info: MOSRowInfo, conn_layer: int, seg: int, w: int, stack: int, g_on_s: bool, options: Param) -> MOSLayInfo: assert conn_layer == 1, 'currently only work for conn_layer = 1' lch = self.lch sd_pitch = self.sd_pitch mp_h: int = self.mos_config['mp_h'] mp_po_extx: int = self.mos_config['mp_po_extx'] md_w: int = self.mos_config['md_w'] mos_lay_table = self.tech_info.config['mos_lay_table'] mp_lp = mos_lay_table['MP'] g_info = self.get_conn_info(1, True) d_info = self.get_conn_info(1, False) export_mid = options.get('export_mid', False) export_mid = export_mid and stack == 2 row_type = row_info.row_type ds_yb, ds_yt = row_info.ds_conn_y threshold = row_info.threshold mp_yb, mp_yt = row_info['mp_y'] md_yb, md_yt = row_info['md_y'] fg = seg * stack wire_pitch = stack * sd_pitch conn_pitch = 2 * wire_pitch num_s = seg // 2 + 1 num_d = (seg + 1) // 2 s_xc = 0 d_xc = wire_pitch if g_on_s: num_g = fg // 2 + 1 g_xc = 0 else: num_g = (fg + 1) // 2 g_xc = sd_pitch g_pitch = 2 * sd_pitch builder = LayoutInfoBuilder() bbox = self._get_mos_active_rect_list(builder, row_info, fg, w, row_info.row_type) # Connect gate to MP mp_po_dx = (sd_pitch + lch) // 2 + mp_po_extx builder.add_rect_arr(mp_lp, BBox(g_xc - mp_po_dx, mp_yb, g_xc + mp_po_dx, mp_yt), nx=num_g, spx=g_pitch) # connect gate to M1. mp_yc = (mp_yb + mp_yt) // 2 builder.add_via(g_info.get_via_info('M1_LiPo', g_xc, mp_yc, mp_h, nx=num_g, spx=g_pitch)) # connect drain/source to M1 m1_yc = (md_yb + md_yt) // 2 via_pitch = d_info.via_h + d_info.via_sp vnum1 = (md_yt - md_yb - d_info.via_bot_enc * 2 + d_info.via_sp) // via_pitch vnum2 = (ds_yt - ds_yb - d_info.via_top_enc * 2 + d_info.via_sp) // via_pitch vnum = min(vnum1, vnum2) builder.add_via(d_info.get_via_info('M1_LiAct', d_xc, m1_yc, md_w, ortho=False, num=vnum, nx=num_d, spx=conn_pitch)) builder.add_via(d_info.get_via_info('M1_LiAct', s_xc, m1_yc, md_w, ortho=False, num=vnum, nx=num_s, spx=conn_pitch)) if export_mid: m_xc = sd_pitch num_m = fg + 1 - num_s - num_d m_info = (m_xc, num_m, wire_pitch) builder.add_via(d_info.get_via_info('M1_LiAct', m_xc, m1_yc, md_w, ortho=False, num=vnum, nx=num_m, spx=wire_pitch)) else: m_info = None edge_info = MOSEdgeInfo(mos_type=row_type, has_od=True, is_sub=False) be = BlkExtInfo(row_type, threshold, False, ImmutableList([(fg, row_type)]), ImmutableSortedDict()) return MOSLayInfo(builder.get_info(bbox), edge_info, edge_info, be, be, g_info=(g_xc, num_g, g_pitch), d_info=(d_xc, num_d, conn_pitch), s_info=(s_xc, num_s, conn_pitch), m_info=m_info, shorted_ports=ImmutableList([MOSPortType.G]))
def _create_masters( self, pinfo: MOSBasePlaceInfo, ridx_p: int, ridx_n: int, is_guarded: bool, invp_params_list: Sequence[Param], invn_params_list: Sequence[Param], pg_params: Param, sig_locs: Mapping[str, Union[float, HalfInt]], vertical_out: bool, vertical_in: bool ) -> Tuple[Sequence[InvCore], Sequence[InvCore], PassGateCore]: # configure sig_locs dictionary so we can connect more signals on hm_layer nout_tid = get_adj_tidx_list(self, ridx_n, sig_locs, MOSWireType.DS, 'nout', False) pout_tid = get_adj_tidx_list(self, ridx_p, sig_locs, MOSWireType.DS, 'pout', True) nin_tid = get_adj_tidx_list(self, ridx_n, sig_locs, MOSWireType.G, 'nin', True) pin_tid = get_adj_tidx_list(self, ridx_p, sig_locs, MOSWireType.G, 'pin', False) # TODO: this gate index hack fixes cases where you cannot short adjacent hm_layer # TODO: tracks with vm_layer. Need more rigorous checking later so we can reduce # TODO: gate resistance even_gate_index = int(is_guarded) # create masters append_dict = dict( pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, is_guarded=is_guarded, ) sig_locs0 = dict( nout=nout_tid[0], pout=pout_tid[0], nin=nin_tid[even_gate_index], pin=pin_tid[even_gate_index], ) sig_locs1 = dict( nout=nout_tid[1], pout=pout_tid[1], nin=nin_tid[1], pin=pin_tid[1], ) invp0 = self.new_template(InvCore, params=invp_params_list[0].copy( append=dict(sig_locs=sig_locs0, vertical_out=not is_guarded, vertical_in=vertical_in, **append_dict))) pg = self.new_template(PassGateCore, params=pg_params.copy(append=dict( sig_locs=dict( nd=nout_tid[0], pd=pout_tid[0], ns=nout_tid[1], ps=pout_tid[1], en=nin_tid[1], enb=pin_tid[1], ), pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, is_guarded=is_guarded, vertical_out=False, vertical_in=False, ))) invp1 = self.new_template( InvCore, params=invp_params_list[1].copy(append=dict( sig_locs=sig_locs0, vertical_out=vertical_out, **append_dict))) invn0 = self.new_template(InvCore, params=invn_params_list[0].copy( append=dict(sig_locs=sig_locs0, vertical_out=not is_guarded, vertical_in=vertical_in, **append_dict))) invn1 = self.new_template( InvCore, params=invn_params_list[1].copy(append=dict( sig_locs=sig_locs1, vertical_out=not is_guarded, ** append_dict))) invn2 = self.new_template( InvCore, params=invn_params_list[2].copy(append=dict( sig_locs=sig_locs0, vertical_out=vertical_out, **append_dict))) if is_guarded: # make sure vm input pin are on the same track n0_in = invn0.get_port('in').get_pins()[0] p0_in = invp0.get_port('in').get_pins()[0] n0_tidx = n0_in.track_id.base_index p0_tidx = p0_in.track_id.base_index if n0_tidx < p0_tidx: sig_locs0['in'] = n0_tidx invp0 = invp0.new_template_with(sig_locs=sig_locs0) elif p0_tidx < n0_tidx: sig_locs0['in'] = p0_tidx invn0 = invn0.new_template_with(sig_locs=sig_locs0) return [invp0, invp1], [invn0, invn1, invn2], pg