def __init__(self, row_specs: MOSRowSpecs, row_info: MOSRowInfo, bot_conn_y_table: Mapping[MOSWireType, Tuple[int, int]], top_conn_y_table: Mapping[MOSWireType, Tuple[int, int]], bot_ext_info: RowExtInfo, top_ext_info: RowExtInfo): # work around: this is how you set attributes for frozen data classes object.__setattr__(self, 'row_specs', row_specs) object.__setattr__(self, 'row_info', row_info) object.__setattr__(self, 'bot_conn_y_table', ImmutableSortedDict(bot_conn_y_table)) object.__setattr__(self, 'top_conn_y_table', ImmutableSortedDict(top_conn_y_table)) object.__setattr__(self, 'bot_ext_info', bot_ext_info) object.__setattr__(self, 'top_ext_info', top_ext_info)
def _update_dut_params(pinfo: Mapping[str, Any], nbits: int, seg_unit: int, seg_buf: int, seg_nand: int, seg_inv: int, num_cores: int, dc_params: Mapping[str, Any]) -> Param: return ImmutableSortedDict( dict( pinfo=pinfo, pi_params=dict( unit_params={ 'seg': seg_unit, 'stack_p': 1, 'stack_n': 1 }, inv_params={ 'seg': seg_buf, 'stack_p': 1, 'stack_n': 1 }, abut_tristates=True, ), dc_params=dc_params, inv_params=dict(seg=seg_inv), nand_params=dict(seg=seg_nand), num_core=num_cores, nbits=nbits, export_dc_out=True, export_dc_in=True, draw_sub=True, # export_outb=True, ))
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: TemplateBase.__init__(self, temp_db, params, **kwargs) self.prim_top_layer = self.grid.bot_layer self._corner: Optional[Tuple[int, int]] = None self._edgel = self._edgeb = ImmutableSortedDict()
def get_blk_info(self, conn_layer: int, w: int, h: int, nx: int, ny: int, **kwargs: Any) -> Optional[ArrayLayInfo]: res_type: str = kwargs.get('res_type', 'standard') if res_type != 'metal': raise ValueError(f'unsupported resistor type: {res_type}') x_pitch: int = self.res_config['x_pitch'] conn_w: int = self.res_config['conn_w'] sp_le = self.tech_info.get_min_line_end_space('M1CA', conn_w, purpose='drawing', even=True) builder = LayoutInfoBuilder() x0 = x_pitch // 2 yl = sp_le yh = h - sp_le x1 = w - x_pitch // 2 boxl = BBox(x0 - conn_w // 2, yl, x0 + conn_w // 2, yh) builder.add_rect_arr(('M1CA', 'drawing'), boxl, nx=2, spx=x1 - x0) xm = w // 2 boxm = BBox(xm - conn_w // 2, yl, xm + conn_w // 2, yh) builder.add_rect_arr(('M1CA', 'drawing'), boxm) boxr = BBox(boxm.xl, yl + conn_w, boxm.xh, yh - conn_w) builder.add_rect_arr(('m1res', 'drawing'), boxr) builder.add_rect_arr(('M1CA', 'drawing'), BBox(boxl.xl, yl, boxm.xh, yl + conn_w)) boxr = boxl.get_move_by(dx=x1 - x0) builder.add_rect_arr(('M1CA', 'drawing'), BBox(boxm.xl, yh - conn_w, boxr.xh, yh)) tidu = HalfInt(2 * ((w // x_pitch) - 1)) return ArrayLayInfo( builder.get_info(BBox(0, 0, w, h)), ImmutableSortedDict({ 'u': WireArrayInfo(1, tidu, yl, yh, 1, 1, 0), 'l': WireArrayInfo(1, HalfInt(0), yl, yh, 1, 1, 0) }), ImmutableSortedDict(), ImmutableSortedDict())
def copy_with(self, row_type: Optional[MOSType] = None, threshold: Optional[str] = None, **kwargs) -> RowExtInfo: if row_type is None: row_type = self.row_type if threshold is None: threshold = self.threshold info_dict = self.info.to_dict() info_dict.update(kwargs) return RowExtInfo(row_type, threshold, ImmutableSortedDict(info_dict))
def _get_w_th_dict( self, ridx_n: int, ridx_p: int ) -> Tuple[ImmutableSortedDict[str, int], ImmutableSortedDict[str, str]]: w_dict: Mapping[str, int] = self.params['w_dict'] w_ans = {} th_ans = {} for name, row_idx in [('tail', ridx_n), ('in', ridx_n + 1), ('pfb', ridx_p)]: rinfo = self.get_row_info(row_idx, 0) w = w_dict.get(name, 0) if w == 0: w = rinfo.width w_ans[name] = w th_ans[name] = rinfo.threshold w_ans['br'] = w_ans['nfb'] = w_ans['in'] w_ans['swm'] = w_ans['swo'] = w_ans['pfb'] th_ans['br'] = th_ans['nfb'] = th_ans['in'] th_ans['swm'] = th_ans['swo'] = th_ans['pfb'] return ImmutableSortedDict(w_ans), ImmutableSortedDict(th_ans)
def _get_w_th_dict(self, ridx_n: int, ridx_p: int, has_rstb: bool ) -> Tuple[ImmutableSortedDict[str, int], ImmutableSortedDict[str, str]]: w_dict: Mapping[str, int] = self.params['w_dict'] w_ans = {} th_ans = {} for row_idx, name_list in [(ridx_n, ['nfb', 'nr']), (ridx_p, ['pfb', 'ps'])]: rinfo = self.get_row_info(row_idx, 0) for name in name_list: w = w_dict.get(name, 0) if w == 0: w = rinfo.width w_ans[name] = w th_ans[name] = rinfo.threshold if has_rstb: w_ans['pr'] = w_ans['ps'] th_ans['pr'] = th_ans['ps'] return ImmutableSortedDict(w_ans), ImmutableSortedDict(th_ans)
def __init__(self, data: Dict[Tuple[str, int], Tuple[HalfInt, int]], ranges: Optional[Dict[str, List[int]]] = None) -> None: self._data: ImmutableSortedDict[Tuple[str, int], Tuple[HalfInt, int]] = ImmutableSortedDict(data) if ranges is None: self._ranges = {} for name, idx in data.keys(): cur_range = self._ranges.get(name, None) if cur_range is None: self._ranges[name] = [idx, idx] else: cur_range[0] = min(cur_range[0], idx) cur_range[1] = max(cur_range[1], idx) else: self._ranges = ranges
def get_mos_corner_info(self, blk_w: int, blk_h: int, einfo: MOSEdgeInfo) -> CornerLayInfo: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] mos_lay_table = self.tech_info.config['mos_lay_table'] cpo_lp = mos_lay_table['CPO'] cpo_h2 = cpo_h // 2 blk_rect = BBox(0, 0, blk_w, blk_h) builder = LayoutInfoBuilder() builder.add_rect_arr(cpo_lp, BBox(blk_w - sd_pitch, blk_h - cpo_h2, blk_w, blk_h + cpo_h2)) edgel = edgeb = ImmutableSortedDict(dict(dev_type=DeviceType.MOS, well_margin=sd_pitch)) return CornerLayInfo(builder.get_info(blk_rect), (0, 0), edgel, edgeb)
def __init__(self, mos_type: MOSType, width: int, threshold: str, bot_wires: WireData, top_wires: WireData, options: Optional[Mapping[str, Any]] = None, flip: bool = False, sub_width: int = 0) -> None: if sub_width == 0 or mos_type.is_substrate: sub_width = width # work around: this is how you set attributes for frozen data classes object.__setattr__(self, 'mos_type', mos_type) object.__setattr__(self, 'width', width) object.__setattr__(self, 'threshold', threshold) object.__setattr__(self, 'bot_wires', bot_wires) object.__setattr__(self, 'top_wires', top_wires) object.__setattr__(self, 'options', ImmutableSortedDict(options)) object.__setattr__(self, 'flip', flip) object.__setattr__(self, 'sub_width', sub_width)
def from_dict(cls, table: Mapping[str, Any]) -> MOSRowInfo: row_type = MOSType[table['row_type']] top_ext_info = RowExtInfo.from_dict(table['top_ext_info']) bot_ext_info = RowExtInfo.from_dict(table['bot_ext_info']) return MOSRowInfo(table['lch'], table['width'], table['sub_width'], row_type, table['threshold'], table['height'], table['flip'], top_ext_info, bot_ext_info, ImmutableSortedDict(table['info']), table['g_conn_y'], table['g_m_conn_y'], table['ds_conn_y'], table['ds_m_conn_y'], table['ds_g_conn_y'], table['sub_conn_y'], guard_ring=table.get('guard_ring', False))
def get_end_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any) -> ArrayEndInfo: builder = LayoutInfoBuilder() return ArrayEndInfo(builder.get_info(BBox(0, 0, w, h)), ImmutableSortedDict())
def get_info(self, bnd_box: BBox) -> LayoutInfo: return LayoutInfo(ImmutableSortedDict(self._lp_dict), ImmutableList(self._warr_list), ImmutableList(self._via_list), bnd_box)
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict'] ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] vertical_sel: bool = self.params['vertical_sel'] w_dict = self._get_w_dict(ridx_n, ridx_p) seg_tri = seg_dict['tri'] seg_buf = seg_dict['buf'] w_pbuf = w_dict['pbuf'] w_nbuf = w_dict['nbuf'] w_ptri = w_dict['ptri'] w_ntri = w_dict['ntri'] if (seg_tri & 1) or (seg_buf % 4 != 0): raise ValueError( 'segments of transistors must be even, buf must be multiple of 4.' ) seg_buf_half = seg_buf // 2 # placement vm_layer = self.conn_layer + 2 grid = self.grid tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') vss_tid = self.get_track_id(ridx_n, False, wire_name='sup') vdd_tid = self.get_track_id(ridx_p, True, wire_name='sup') nd_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) pd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) ng2_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2) pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2) pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1) in_tid = ng2_tid pmid_tid = pg0_tid psel_tid = pg1_tid tri_ncol = 2 * seg_tri min_sep = self.min_sep_col min_sep += (min_sep & 1) ntri = self.add_mos(ridx_n, 0, seg_tri, w=w_ntri, stack=2, g_on_s=True, sep_g=True) ptri = self.add_mos(ridx_p, 0, seg_tri, w=w_ptri, stack=2, g_on_s=True, sep_g=True) cur_col = tri_ncol + min_sep nbuf = self.add_mos(ridx_n, cur_col, seg_buf_half, w=w_nbuf) pbuf = self.add_mos(ridx_p, cur_col, seg_buf_half, w=w_pbuf) self.set_mos_size() # routing # supplies vdd = self.connect_to_tracks([ptri.s, pbuf.s], vdd_tid) vss = self.connect_to_tracks([ntri.s, nbuf.s], vss_tid) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # select signals in_warr = self.connect_to_tracks([ntri.g[0::2], ptri.g[0::2]], in_tid) nsel = ntri.g[1::2] psel = self.connect_to_tracks(ptri.g[1::2], psel_tid, min_len_mode=MinLenMode.MIDDLE) vm_tidx = grid.coord_to_track(vm_layer, psel.middle, mode=RoundMode.GREATER_EQ) if vertical_sel: psel_vm = self.connect_to_tracks(psel, TrackID(vm_layer, vm_tidx, width=vm_w), min_len_mode=MinLenMode.LOWER) self.add_pin('psel_vm', psel_vm) self.add_pin('in', in_warr) self.add_pin('nsel', nsel) self.add_pin('psel', psel) # mid pmid = self.connect_to_tracks(ptri.d, pd_tid, min_len_mode=MinLenMode.UPPER) nmid = self.connect_to_tracks(ntri.d, nd_tid, min_len_mode=MinLenMode.UPPER) vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx, 'sig', 'sig', up=True) mid = self.connect_to_tracks([pmid, nmid], TrackID(vm_layer, vm_tidx, width=vm_w)) mid = self.connect_to_tracks([mid, nbuf.g, pbuf.g], pmid_tid) self.add_pin('mid', mid) # output pout = self.connect_to_tracks(pbuf.d, pd_tid, min_len_mode=MinLenMode.UPPER) nout = self.connect_to_tracks(nbuf.d, nd_tid, min_len_mode=MinLenMode.UPPER) vm_tidx = grid.coord_to_track(vm_layer, self.bound_box.xh) out = self.connect_to_tracks([pout, nout], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('out', out) lch = self.arr_info.lch th_p = self.get_row_info(ridx_p, 0).threshold th_n = self.get_row_info(ridx_n, 0).threshold self.sch_params = dict( inv_params=ImmutableSortedDict( dict( lch=lch, seg_p=seg_buf, seg_n=seg_buf, w_p=w_pbuf, w_n=w_nbuf, th_p=th_p, th_n=th_n, )), tri_params=ImmutableSortedDict( dict( lch=lch, seg=seg_tri, w_p=w_ptri, w_n=w_ntri, th_p=th_p, th_n=th_n, )), )
def draw_layout(self): place_info = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(place_info) seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict'] ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] has_rstb: bool = self.params['has_rstb'] has_outbuf: bool = self.params['has_outbuf'] has_inbuf: bool = self.params['has_inbuf'] out_pitch: HalfInt = HalfInt.convert(self.params['out_pitch']) w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p, has_rstb) seg_fb = seg_dict['fb'] seg_ps = seg_dict['ps'] seg_nr = seg_dict['nr'] seg_obuf = seg_dict['obuf'] if has_outbuf else 0 seg_ibuf = seg_dict['ibuf'] if has_inbuf else 0 w_pfb = w_dict['pfb'] w_nfb = w_dict['nfb'] w_ps = w_dict['ps'] w_nr = w_dict['nr'] w_rst = w_dict.get('pr', 0) w_nbuf = w_nr w_pbuf = w_ps sch_seg_dict = dict(nfb=seg_fb, pfb=seg_fb, ps=seg_ps, nr=seg_nr) if has_rstb: sch_seg_dict['pr'] = seg_rst = seg_dict['rst'] else: seg_rst = 0 if seg_ps & 1 or seg_nr & 1 or seg_rst & 1 or seg_obuf & 1: raise ValueError( 'ps, nr, rst, and buf must have even number of segments') # placement min_sep = self.min_sep_col # use even step size to maintain supply conn_layer wires parity. min_sep += (min_sep & 1) hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 grid = self.grid arr_info = self.arr_info tr_manager = self.tr_manager hm_w = tr_manager.get_width(hm_layer, 'sig') vm_w = tr_manager.get_width(vm_layer, 'sig') hm_sep_col = self.get_hm_sp_le_sep_col(ntr=hm_w) mid_sep = max(hm_sep_col - 2, 0) mid_sep = (mid_sep + 1) // 2 if has_inbuf: m_nibuf = self.add_mos(ridx_n, mid_sep, seg_ibuf, w=w_nbuf) m_pibuf = self.add_mos(ridx_p, mid_sep, seg_ibuf, w=w_pbuf) cur_col = mid_sep + seg_ibuf psrb_list = [m_nibuf.g, m_pibuf.g] else: m_nibuf = m_pibuf = None cur_col = mid_sep psrb_list = [] nr_col = cur_col m_nr = self.add_mos(ridx_n, cur_col, seg_nr, w=w_nr) m_ps = self.add_mos(ridx_p, cur_col, seg_ps, w=w_ps) psrb_list.append(m_ps.g) pcol = cur_col + seg_ps if has_rstb: m_rst = self.add_mos(ridx_p, pcol, seg_rst, w=w_rst) pcol += seg_rst else: m_rst = None cur_col = max(cur_col + seg_nr, pcol) if has_outbuf: m_pinv = self.add_mos(ridx_p, cur_col, seg_obuf, w=w_pbuf) m_ninv = self.add_mos(ridx_n, cur_col, seg_obuf, w=w_nbuf) cur_col += seg_obuf else: m_pinv = m_ninv = None cur_col += min_sep fb_col = cur_col m_pfb = self.add_mos(ridx_p, cur_col, seg_fb, w=w_pfb, g_on_s=True, stack=2, sep_g=True) m_nfb = self.add_mos(ridx_n, cur_col, seg_fb, w=w_nfb, g_on_s=True, stack=2, sep_g=True) self.set_mos_size() # track planning vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nbuf_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-2) nq_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) psr_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1) pq_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pbuf_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sup') # try to spread out gate wires to lower parasitics on differential Q wires ng_lower = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) ng_upper = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) g_idx_list = tr_manager.spread_wires( hm_layer, ['sig', 'sig_hs', 'sig_hs', 'sig'], ng_lower, ng_upper, ('sig_hs', 'sig_hs'), alignment=0) self._q_tr_info = (hm_w, g_idx_list[2], g_idx_list[1]) self._sr_hm_tr_info = (hm_w, g_idx_list[3], g_idx_list[0]) if has_rstb: rst_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2) pq_conn_list = [m_ps.d, m_rst.d, m_pfb.s] vdd_list = [m_ps.s, m_rst.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] rstb = self.connect_to_tracks(m_rst.g, rst_tid, min_len_mode=MinLenMode.MIDDLE) rst_vm_tidx = grid.coord_to_track(vm_layer, rstb.middle, mode=RoundMode.GREATER_EQ) rstb_vm = self.connect_to_tracks( rstb, TrackID(vm_layer, rst_vm_tidx, width=vm_w)) self.add_pin('rstb', rstb_vm) else: pq_conn_list = [m_ps.d, m_pfb.s] vdd_list = [m_ps.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] self.add_pin('nsr', m_nr.g) self.add_pin('nsrb', m_nfb.g[0::2]) nq = self.connect_to_tracks([m_nr.d, m_nfb.s], nq_tid) pq = self.connect_to_tracks(pq_conn_list, pq_tid) psrb = self.connect_to_tracks(psrb_list, psr_tid, min_len_mode=MinLenMode.UPPER) psr = self.connect_to_tracks(m_pfb.g[0::2], psr_tid, min_len_mode=MinLenMode.LOWER) qb = self.connect_wires([m_nfb.g[1::2], m_pfb.g[1::2]]) self.add_pin('qb', qb) if has_inbuf: vdd_list.append(m_pibuf.s) vss_list.append(m_nibuf.s) nbuf = self.connect_to_tracks(m_nibuf.d, nbuf_tid, min_len_mode=MinLenMode.UPPER) pbuf = self.connect_to_tracks(m_pibuf.d, pbuf_tid, min_len_mode=MinLenMode.UPPER) vm_tidx = grid.coord_to_track(vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('sr_buf', buf) out_p_htr = out_pitch.dbl_value vm_ref = grid.coord_to_track(vm_layer, 0) srb_vm_tidx = arr_info.col_to_track(vm_layer, nr_col + 1, mode=RoundMode.GREATER_EQ) if has_outbuf: vdd_list.append(m_pinv.s) vss_list.append(m_ninv.s) nbuf = self.connect_to_tracks(m_ninv.d, nbuf_tid, min_len_mode=MinLenMode.MIDDLE) pbuf = self.connect_to_tracks(m_pinv.d, pbuf_tid, min_len_mode=MinLenMode.MIDDLE) vm_delta = grid.coord_to_track( vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr vm_tidx = vm_ref + HalfInt(vm_htr) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('buf_out', buf) buf_in = self.connect_wires([m_ninv.g, m_pinv.g]) self.add_pin('buf_in', buf_in) q_vm_tidx = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') else: vm_delta = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr q_vm_tidx = vm_ref + HalfInt(vm_htr) sr_vm_tidx = arr_info.col_to_track(vm_layer, fb_col, mode=RoundMode.LESS_EQ) self._sr_vm_tr_info = (vm_w, sr_vm_tidx, srb_vm_tidx) q_vm = self.connect_to_tracks([nq, pq], TrackID(vm_layer, q_vm_tidx, width=vm_w)) self.add_pin('q_vm', q_vm) self.add_pin('psr', psr) self.add_pin('psrb', psrb) self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss_list, vss_tid)) lch = arr_info.lch buf_params = ImmutableSortedDict( dict( lch=lch, w_p=w_pbuf, w_n=w_nbuf, th_p=th_dict['ps'], th_n=th_dict['nr'], seg=seg_obuf, )) obuf_params = buf_params if has_outbuf else None ibuf_params = buf_params.copy(append=dict( seg=seg_ibuf)) if has_inbuf else None self.sch_params = dict( core_params=ImmutableSortedDict( dict( lch=lch, seg_dict=ImmutableSortedDict(sch_seg_dict), w_dict=w_dict, th_dict=th_dict, )), outbuf_params=obuf_params, inbuf_params=ibuf_params, has_rstb=has_rstb, )
def __init__(self, **kwargs: Any) -> None: object.__setattr__(self, 'info', ImmutableSortedDict(kwargs))
def get_mos_row_info(self, conn_layer: int, specs: MOSRowSpecs, bot_mos_type: MOSType, top_mos_type: MOSType, global_options: Param) -> MOSRowInfo: guard_ring: bool = specs.options.get('guard_ring', False) assert conn_layer == 1, 'currently only work for conn_layer = 1' fin_p = self.mos_config['fin_p'] mp_cpo_spy: int = self.mos_config['mp_cpo_spy'] mp_h: int = self.mos_config['mp_h'] mp_spy: int = self.mos_config['mp_spy'] cpo_od_spy: int = self.mos_config['cpo_od_spy'] cpo_h: int = self.mos_config['cpo_h'] md_spy: int = self.mos_config['md_spy'] md_h_min: int = self.mos_config['md_h_min'] md_od_exty: int = self.mos_config['md_od_exty'] od_spy: int = self.mos_config['od_spy'] mos_type = specs.mos_type w = specs.width w_sub = specs.sub_width od_h = self.get_od_height(w) md_h = max(od_h + 2 * md_od_exty, md_h_min) g_conn_info = self.get_conn_info(conn_layer, True) g_m1_dyt = g_conn_info.via_w // 2 + g_conn_info.via_top_enc g_m1_sp_le = g_conn_info.sp_le d_conn_info = self.get_conn_info(conn_layer, False) d_m1_h = max(md_h, d_conn_info.len_min) # place bottom CPO, compute gate/OD locations blk_yb = 0 cpo_bot_yb = blk_yb - cpo_h // 2 cpo_bot_yt = cpo_bot_yb + cpo_h # get gate via/M1 location mp_yb = max(mp_spy // 2, cpo_bot_yt + mp_cpo_spy) mp_yt = mp_yb + mp_h mp_yc = (mp_yt + mp_yb) // 2 g_m1_yt = mp_yc + g_m1_dyt # get OD location, round to fin grid. od_yb = g_m1_yt + g_m1_sp_le + (d_m1_h - od_h) // 2 od_yb = self.snap_od_edge(od_yb, False, True) od_yt = od_yb + od_h od_yc = (od_yb + od_yt) // 2 md_yb = od_yc - md_h // 2 md_yt = md_yb + md_h # compute top CPO location. blk_yt = od_yt + cpo_od_spy + cpo_h // 2 blk_yt = -(-blk_yt // fin_p) * fin_p conn_info = self.get_conn_yloc_info(conn_layer, md_yb, md_yt, mos_type.is_substrate) m1_yt = conn_info.ds[1] m1_yb = conn_info.g[0] m1_spy = d_conn_info.sp_le top_einfo = RowExtInfo( mos_type, specs.threshold, ImmutableSortedDict(dict( margins=dict( od=(blk_yt - od_yt, od_spy), md=(blk_yt - md_yt, md_spy), m1=(blk_yt - m1_yt, m1_spy), ))), ) bot_einfo = RowExtInfo( specs.mos_type, specs.threshold, ImmutableSortedDict(dict( margins=dict( od=(od_yb, od_spy), md=(md_yb, md_spy), m1=(m1_yb, m1_spy), ))), ) info = dict( od_yb=od_yb, md_y=(md_yb, md_yt), po_y=(cpo_h // 2, blk_yt - cpo_h // 2), mp_y=conn_info.mp, ) return MOSRowInfo(self.lch, w, w_sub, mos_type, specs.threshold, blk_yt, specs.flip, top_einfo, bot_einfo, ImmutableSortedDict(info), conn_info.g, conn_info.g_m, conn_info.ds, conn_info.ds_m, conn_info.ds_g, conn_info.sub, guard_ring=guard_ring and mos_type.is_substrate)
def from_dict(cls, table: Mapping[str, Any]) -> RowExtInfo: return RowExtInfo(MOSType[table['row_type']], table['threshold'], ImmutableSortedDict(table['info']))
def get_mos_space_info(self, row_info: MOSRowInfo, num_cols: int, left_info: MOSEdgeInfo, right_info: MOSEdgeInfo) -> MOSLayInfo: lch = self.lch sd_pitch = self.sd_pitch od_po_extx = self.od_po_extx if num_cols < self.min_sep_col and left_info and right_info: # cannot draw less than min_sep_col between two devices raise ODImplantEnclosureError(f'Cannot draw MOSSpace in {num_cols} columns.') imp_od_encx: int = self.mos_config['imp_od_encx'] mos_lay_table = self.tech_info.config['mos_lay_table'] po_lp = mos_lay_table['PO_DUMMY'] pode_lp = mos_lay_table['PO_PODE'] blk_xr = num_cols * sd_pitch blk_yt = row_info.height bbox = BBox(0, 0, blk_xr, blk_yt) row_type = row_info.row_type threshold = row_info.threshold builder = LayoutInfoBuilder() po_y = (0, blk_yt) od_extx = od_po_extx - (sd_pitch - lch) // 2 if left_info.get('has_od', False): self._add_po_array(builder, pode_lp, po_y, 0, 1) po_start = 1 imp_xmin = od_extx + imp_od_encx else: po_start = 0 imp_xmin = 0 if right_info.get('has_od', False): self._add_po_array(builder, pode_lp, po_y, num_cols - 1, num_cols) po_stop = num_cols - 1 imp_xmax = blk_xr - od_extx - imp_od_encx else: po_stop = num_cols imp_xmax = blk_xr self._add_po_array(builder, po_lp, (0, blk_yt), po_start, po_stop) self._add_fb(builder, bbox) if left_info: typel = left_info['mos_type'] if right_info: typer = right_info['mos_type'] else: typer = typel else: if right_info: typer = right_info['mos_type'] else: typer = row_type typel = typer guard_ring = ((typel.is_substrate and typel is not row_type.sub_type) or (typer.is_substrate and typer is not row_type.sub_type)) if typel == typer: if guard_ring: raise ValueError('Cannot have empty spaces between guard ring edges.') blk_rect = BBox(0, 0, blk_xr, blk_yt) for lay_purp in self._thres_imp_well_layers_iter(row_type, typel, threshold): builder.add_rect_arr(lay_purp, blk_rect) be = BlkExtInfo(row_type, threshold, False, ImmutableList([(num_cols, typel)]), ImmutableSortedDict()) edgel = edger = MOSEdgeInfo(mos_type=typel, has_od=False, is_sub=False) else: # must be in transistor row, and has one substrate if typel.is_substrate and ((not typer.is_substrate) or typel is not row_type.sub_type): xmid = od_extx + imp_od_encx else: xmid = blk_xr - od_extx - imp_od_encx if xmid < imp_xmin or xmid > imp_xmax: raise ODImplantEnclosureError('Insufficient space to satisfy implant-OD ' 'horizontal enclosure.') rectl = BBox(0, 0, xmid, blk_yt) rectr = BBox(xmid, 0, blk_xr, blk_yt) for lay_purp in self._thres_imp_well_layers_iter(row_type, typel, threshold): builder.add_rect_arr(lay_purp, rectl) for lay_purp in self._thres_imp_well_layers_iter(row_type, typer, threshold): builder.add_rect_arr(lay_purp, rectr) fgl = xmid // sd_pitch be = BlkExtInfo(row_type, threshold, guard_ring, ImmutableList([(fgl, typel), (num_cols - fgl, typer)]), ImmutableSortedDict()) edgel = MOSEdgeInfo(mos_type=typel, has_od=False, is_sub=False) edger = MOSEdgeInfo(mos_type=typer, has_od=False, is_sub=False) wire_info = (0, 0, 0) return MOSLayInfo(builder.get_info(bbox), edgel, edger, be, be, g_info=wire_info, d_info=wire_info, s_info=wire_info, shorted_ports=ImmutableList())
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]))