Source code for xbase.layout.mos.primitives

# SPDX-License-Identifier: Apache-2.0
# Copyright 2019 Blue Cheetah Analog Design Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Dict, Any, Optional, Tuple

from bag.util.immutable import Param, ImmutableList, ImmutableSortedDict
from bag.layout.template import TemplateBase, TemplateDB

from ..enum import MOSPortType, MOSType
from ..data import draw_layout_in_template

from .tech import MOSTech
from .data import MOSEdgeInfo, RowExtInfo, BlkExtInfo, MOSRowInfo, MOSLayInfo


[docs]class MOSConn(TemplateBase): """Transistor connection primitive. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: TemplateBase.__init__(self, temp_db, params, **kwargs) self._left_info: Optional[MOSEdgeInfo] = None self._right_info: Optional[MOSEdgeInfo] = None self._top_info: Optional[BlkExtInfo] = None self._bottom_info: Optional[BlkExtInfo] = None self._shorted_ports: Optional[ImmutableList[MOSPortType]] = None @property
[docs] def left_info(self) -> Optional[MOSEdgeInfo]: return self._left_info
@property
[docs] def right_info(self) -> Optional[MOSEdgeInfo]: return self._right_info
@property
[docs] def top_info(self) -> Optional[BlkExtInfo]: return self._top_info
@property
[docs] def bottom_info(self) -> Optional[BlkExtInfo]: return self._bottom_info
@property
[docs] def shorted_ports(self) -> ImmutableList[MOSPortType]: return self._shorted_ports
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( row_info='transistor row information dictionary.', conn_layer='connection layer ID.', seg='number of segments.', w='transistor width, in resolution units or fins.', stack='number of transistors in a stack.', g_on_s='True if gate is aligned with source.', options='Optional process-specific parameters.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: row_info: MOSRowInfo = self.params['row_info'] conn_layer: int = self.params['conn_layer'] seg: int = self.params['seg'] w: int = self.params['w'] stack: int = self.params['stack'] g_on_s: bool = self.params['g_on_s'] options: Param = self.params['options'] arr_options: Param = self.params['arr_options'] grid = self.grid tech_cls: MOSTech = grid.tech_info.get_device_tech('mos', lch=row_info.lch, arr_options=arr_options) mos_info: MOSLayInfo = tech_cls.get_mos_conn_info(row_info, conn_layer, seg, w, stack, g_on_s, options) draw_layout_in_template(self, mos_info.lay_info) g_conn_y = row_info.g_conn_y g2_conn_y = row_info.g2_conn_y if g_on_s: if (stack & 1) == 0: d_conn_y = row_info.ds_g_conn_y else: d_conn_y = row_info.ds_conn_y s_conn_y = row_info.ds_g_conn_y else: d_conn_y = row_info.ds_g_conn_y s_conn_y = row_info.ds_conn_y g_xc, num_g, g_pitch = mos_info.g_info d_xc, num_d, d_pitch = mos_info.d_info s_xc, num_s, s_pitch = mos_info.s_info pitch = grid.get_track_pitch(conn_layer) g_tr = grid.coord_to_track(conn_layer, g_xc) d_tr = grid.coord_to_track(conn_layer, d_xc) s_tr = grid.coord_to_track(conn_layer, s_xc) g_tr_p = g_pitch // pitch d_tr_p = d_pitch // pitch s_tr_p = s_pitch // pitch dw = self.add_wires(conn_layer, d_tr, d_conn_y[0], d_conn_y[1], num=num_d, pitch=d_tr_p) sw = self.add_wires(conn_layer, s_tr, s_conn_y[0], s_conn_y[1], num=num_s, pitch=s_tr_p) self.add_pin('d', dw) self.add_pin('s', sw) """Double gate options: draw_g enables drawing g. draw_g2 enables drawing g2. Defaults to drawing both""" draw_g = options.get('draw_g', True) draw_g2 = options.get('draw_g2', True) if not row_info.double_gate or draw_g: gw = self.add_wires(conn_layer, g_tr, g_conn_y[0], g_conn_y[1], num=num_g, pitch=g_tr_p) self.add_pin('g', gw) if row_info.double_gate and draw_g2: g2w = self.add_wires(conn_layer, g_tr, g2_conn_y[0], g2_conn_y[1], num=num_g, pitch=g_tr_p) self.add_pin('g2', g2w) if mos_info.m_info is not None: m_xc, num_m, m_pitch = mos_info.m_info m_tr = grid.coord_to_track(conn_layer, m_xc) m_tr_p = m_pitch // pitch m_conn_y = row_info.ds_conn_y self.add_pin('m', self.add_wires(conn_layer, m_tr, m_conn_y[0], m_conn_y[1], num=num_m, pitch=m_tr_p)) self.prim_top_layer = conn_layer self._left_info = mos_info.left_info self._right_info = mos_info.right_info self._top_info = mos_info.top_info self._bottom_info = mos_info.bottom_info self._shorted_ports = mos_info.shorted_ports
[docs]class MOSTap(TemplateBase): """Transistor substrate tap primitive. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: TemplateBase.__init__(self, temp_db, params, **kwargs) self._left_info: Optional[MOSEdgeInfo] = None self._right_info: Optional[MOSEdgeInfo] = None self._top_info: Optional[BlkExtInfo] = None self._bottom_info: Optional[BlkExtInfo] = None @property
[docs] def left_info(self) -> Optional[MOSEdgeInfo]: return self._left_info
@property
[docs] def right_info(self) -> Optional[MOSEdgeInfo]: return self._right_info
@property
[docs] def top_info(self) -> Optional[BlkExtInfo]: return self._top_info
@property
[docs] def bottom_info(self) -> Optional[BlkExtInfo]: return self._bottom_info
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( row_info='transistor row information dictionary.', conn_layer='connection layer ID.', seg='number of segments.', options='Optional process-specific parameters.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: row_info: MOSRowInfo = self.params['row_info'] conn_layer: int = self.params['conn_layer'] seg: int = self.params['seg'] options: Param = self.params['options'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=row_info.lch, arr_options=arr_options) mos_info = tech_cls.get_mos_tap_info(row_info, conn_layer, seg, options) draw_layout_in_template(self, mos_info.lay_info) conn_y = row_info.sub_conn_y sup = self.add_wires(conn_layer, -0.5, conn_y[0], conn_y[1], num=seg + 1, pitch=1) self.add_pin('sup', sup, show=False) self.prim_top_layer = conn_layer self._left_info = mos_info.left_info self._right_info = mos_info.right_info self._top_info = mos_info.top_info self._bottom_info = mos_info.bottom_info
[docs]class MOSAbut(TemplateBase): """An empty space in a transistor row. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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 @classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( row_info='transistor row information dictionary.', edgel='edge info of the block to the left.', edger='edge info of the block to the right.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: row_info: MOSRowInfo = self.params['row_info'] edgel: MOSEdgeInfo = self.params['edgel'] edger: MOSEdgeInfo = self.params['edger'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=row_info.lch, arr_options=arr_options) lay_info = tech_cls.get_mos_abut_info(row_info, edgel, edger) draw_layout_in_template(self, lay_info) self.disable_cell_boundary()
[docs]class MOSSpace(TemplateBase): """An empty space in a transistor row. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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._left_info: Optional[MOSEdgeInfo] = None self._right_info: Optional[MOSEdgeInfo] = None self._top_info: Optional[BlkExtInfo] = None self._bottom_info: Optional[BlkExtInfo] = None @property
[docs] def left_info(self) -> Optional[MOSEdgeInfo]: return self._left_info
@property
[docs] def right_info(self) -> Optional[MOSEdgeInfo]: return self._right_info
@property
[docs] def top_info(self) -> Optional[BlkExtInfo]: return self._top_info
@property
[docs] def bottom_info(self) -> Optional[BlkExtInfo]: return self._bottom_info
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( row_info='transistor row information dictionary.', num_cols='number of columns.', left_info='edge info of the block to the left.', right_info='edge info of the block to the right.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: row_info: MOSRowInfo = self.params['row_info'] num_cols: int = self.params['num_cols'] left_info: MOSEdgeInfo = self.params['left_info'] right_info: MOSEdgeInfo = self.params['right_info'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=row_info.lch, arr_options=arr_options) mos_info = tech_cls.get_mos_space_info(row_info, num_cols, left_info, right_info) draw_layout_in_template(self, mos_info.lay_info) self._left_info = mos_info.left_info self._right_info = mos_info.right_info self._top_info = mos_info.top_info self._bottom_info = mos_info.bottom_info
[docs]class MOSExt(TemplateBase): """An extension block between transistor rows Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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._edge_info: Optional[MOSEdgeInfo] = None @property
[docs] def edge_info(self) -> Optional[MOSEdgeInfo]: return self._edge_info
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( lch='channel length in resolution units.', num_cols='number of columns.', height='extension height in resolution unit.', bot_info='RowExtInfo object of bottom row.', top_info='RowExtInfo object of top row.', gr_info='Tuple of guard ring edge tap fingers and total guard ring edge fingers.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: lch: int = self.params['lch'] num_cols: int = self.params['num_cols'] height: int = self.params['height'] bot_info: RowExtInfo = self.params['bot_info'] top_info: RowExtInfo = self.params['top_info'] gr_info: Tuple[int, int] = self.params['gr_info'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=lch, arr_options=arr_options) ext_info = tech_cls.get_mos_ext_info(num_cols, height, bot_info, top_info, gr_info) draw_layout_in_template(self, ext_info.lay_info) self._edge_info = ext_info.edge_info
[docs]class MOSExtGR(TemplateBase): """An extension block between transistor rows Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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._edge_info: Optional[MOSEdgeInfo] = None @property
[docs] def edge_info(self) -> Optional[MOSEdgeInfo]: return self._edge_info
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( lch='channel length in resolution units.', num_cols='number of columns.', edge_cols='number of guard ring edge columns.', height='extension height in resolution unit.', bot_info='RowExtInfo object of bottom row.', top_info='RowExtInfo object of top row.', sub_type='guard ring substrate type.', einfo='MOSEdgeInfo object of adjacent MOSExt block.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: params = self.params lch: int = params['lch'] num_cols: int = params['num_cols'] edge_cols: int = params['edge_cols'] height: int = params['height'] bot_info: RowExtInfo = params['bot_info'] top_info: RowExtInfo = params['top_info'] sub_type: MOSType = params['sub_type'] einfo: MOSEdgeInfo = params['einfo'] arr_options: Param = params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=lch, arr_options=arr_options) ext_info = tech_cls.get_mos_ext_gr_info(num_cols, edge_cols, height, bot_info, top_info, sub_type, einfo) draw_layout_in_template(self, ext_info.lay_info) self._edge_info = ext_info.edge_info
[docs]class MOSEnd(TemplateBase): """An end row block of transistor array. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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._einfo: Optional[MOSEdgeInfo] = None @property
[docs] def edge_info(self) -> Optional[MOSEdgeInfo]: return self._einfo
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( lch='channel length in resolution units.', blk_h='end height in resolution units.', num_cols='number of columns.', einfo='RowExtInfo object of adjacent row.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: params = self.params lch: int = params['lch'] blk_h: int = params['blk_h'] num_cols: int = params['num_cols'] einfo: RowExtInfo = params['einfo'] arr_options: Param = params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=lch, arr_options=arr_options) end_info = tech_cls.get_mos_end_info(blk_h, num_cols, einfo) draw_layout_in_template(self, end_info.lay_info) self._einfo = end_info.edge_info
[docs]class MOSRowEdge(TemplateBase): """The edge block of a transistor row. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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 @classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( blk_w='edge width in resolution units.', rinfo='MOSRowInfo object of adjacent row.', einfo='MOSEdgeInfo object of adjacent MOSConn block.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: blk_w: int = self.params['blk_w'] rinfo: MOSRowInfo = self.params['rinfo'] einfo: MOSEdgeInfo = self.params['einfo'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=rinfo.lch, arr_options=arr_options) lay_info = tech_cls.get_mos_row_edge_info(blk_w, rinfo, einfo) draw_layout_in_template(self, lay_info)
[docs]class MOSExtEdge(TemplateBase): """The edge block of the transistor extension region. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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 @classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( lch='channel length in resolution units.', blk_w='edge width in resolution units.', einfo='MOSEdgeInfo object of adjacent MOSExt or MOSExtGR block.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: lch: int = self.params['lch'] blk_w: int = self.params['blk_w'] einfo: MOSEdgeInfo = self.params['einfo'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=lch, arr_options=arr_options) lay_info = tech_cls.get_mos_ext_edge_info(blk_w, einfo) draw_layout_in_template(self, lay_info)
[docs]class MOSCorner(TemplateBase): """The corner block of transistor array. Parameters ---------- temp_db : TemplateDB the template database. params : Param the parameter values. kwargs : dictionary of optional parameters. See documentation of :class:`bag.layout.template.TemplateBase` for details. """ 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() @property
[docs] def corner(self) -> Tuple[int, int]: return self._corner
@property
[docs] def left_edge(self) -> Param: return self._edgel
@property
[docs] def bottom_edge(self) -> Param: return self._edgeb
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( lch='channel length in resolution units.', blk_w='edge width in resolution units.', blk_h='end height in resolution units.', einfo='MOSEdgeInfo object of adjacent MOSEnd block.', arr_options='Optional process-specific parameters for the array.',
)
[docs] def draw_layout(self) -> None: lch: int = self.params['lch'] blk_w: int = self.params['blk_w'] blk_h: int = self.params['blk_h'] einfo: MOSEdgeInfo = self.params['einfo'] arr_options: Param = self.params['arr_options'] tech_cls: MOSTech = self.grid.tech_info.get_device_tech('mos', lch=lch, arr_options=arr_options) corner_info = tech_cls.get_mos_corner_info(blk_w, blk_h, einfo) draw_layout_in_template(self, corner_info.lay_info) self._corner = corner_info.corner self._edgel = corner_info.edgel self._edgeb = corner_info.edgeb