Source code for bag3_analog.layout.drv_shunt_peak

from typing import Mapping, Any, Optional, Type, Union

from bag.util.immutable import Param
from bag.design.module import Module
from bag.layout.template import TemplateDB, TemplateBase
from bag.layout.routing.base import TrackManager, WDictType, SpDictType

from pybag.enum import RoundMode, MinLenMode, Orientation
from pybag.core import Transform, BBox

from xbase.layout.mos.top import GenericWrapper
from xbase.layout.array.top import ArrayBaseWrapper

from bag3_magnetics.layout.inductor.ind_diff_wrap import IndDiffWrap

from .gm_stage import GmStageGR, GmStage
from .res_diff import ResDiff

from ..schematic.drv_shunt_peak import bag3_analog__drv_shunt_peak, LayMode


[docs]class DrvShuntPeak(TemplateBase): def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: TemplateBase.__init__(self, temp_db, params, **kwargs) tr_widths: WDictType = self.params['tr_widths'] tr_spaces: SpDictType = self.params['tr_spaces'] self._tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) @classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: return bag3_analog__drv_shunt_peak
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]: return dict( tr_widths='Track width dictionary for TrackManager', tr_spaces='Track spaces dictionary for TrackManager', gm_params='Parameters for gm cells', res_params='Parameters for differential resistors', ind_sh_params='Parameters for shunt differential inductor', ind_v_sp='Vertical spacing between resistor and inductor', lay_mode='Layout mode, TOP by default.',
) @classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]: return dict(lay_mode=LayMode.TOP, ind_v_sp=0)
[docs] def draw_layout(self) -> None: # make masters gm_params: Mapping[str, Any] = self.params['gm_params'] gm_cls = GmStageGR if self.has_guard_ring else GmStage gm_master = self.new_template(GenericWrapper, params=dict(cls_name=gm_cls.get_qualified_name(), params=gm_params, export_hidden=True)) gm_bbox = gm_master.bound_box res_params: Mapping[str, Any] = self.params['res_params'] res_master = self.new_template(ArrayBaseWrapper, params=dict(cls_name=ResDiff.get_qualified_name(), params=res_params, export_hidden=True)) res_bbox = res_master.bound_box ind_sh_params: Mapping[str, Any] = self.params['ind_sh_params'] ind_sh_master: IndDiffWrap = self.new_template(IndDiffWrap, params=ind_sh_params) ind_sh_bbox = ind_sh_master.actual_bbox ind_layer: int = ind_sh_params['lay_id'] lay_mode: Union[str, LayMode] = self.params['lay_mode'] if isinstance(lay_mode, str): lay_mode = LayMode[lay_mode] gen_gm = gen_res = lay_mode is LayMode.TOP or lay_mode is LayMode.EXT gen_ind = lay_mode is LayMode.TOP or lay_mode is LayMode.EM # --- Placement --- # w_tot = max(gm_bbox.w, res_bbox.w, ind_sh_bbox.w) x_gm = (w_tot - gm_bbox.w) // 2 y_gm = gm_bbox.h gm = self.add_instance(gm_master, xform=Transform(dx=x_gm, dy=y_gm, mode=Orientation.MX), commit=gen_gm) x_res = (w_tot + res_bbox.w) // 2 y_res = y_gm res = self.add_instance(res_master, xform=Transform(dx=x_res, dy=y_res, mode=Orientation.MY), commit=gen_res) ind_v_sp: int = self.params['ind_v_sp'] x_ind = (w_tot + ind_sh_bbox.w) // 2 y_ind = y_res + res_bbox.h + ind_v_sp ind = self.add_instance(ind_sh_master, xform=Transform(dx=x_ind, dy=y_ind, mode=Orientation.MY), commit=gen_ind) h_tot = ind.bound_box.yh self.set_size_from_bound_box(ind_layer, BBox(0, 0, w_tot, h_tot), round_up=True) # --- Routing --- # # gm cell if gen_gm: for pin in ('v_inp', 'v_inm', 'v_tail_g', 'v_tail'): self.reexport(gm.get_port(pin)) self.reexport(gm.get_port('VSS'), connect=True) if gm.has_port('VDD'): # TODO: routing supply self.reexport(gm.get_port('VDD'), connect=True) # res_diff if gen_res: for pin in ('VDD', 'VSS'): if res.has_port(pin): self.reexport(res.get_port(pin), connect=True) # routing from gm cell to res_diff i_outp = gm.get_pin('i_outp') i_outm = gm.get_pin('i_outm') gm_top_layer = i_outp.layer_id p_in = res.get_pin('p_in') p_out = res.get_pin('p_out') m_in = res.get_pin('m_in') m_out = res.get_pin('m_out') if gm_top_layer < p_in.layer_id: raise NotImplementedError elif gm_top_layer > p_in.layer_id: _tidx0 = self.grid.coord_to_track(gm_top_layer, p_in.bound_box.ym, RoundMode.NEAREST) _tidx1 = self.grid.coord_to_track(gm_top_layer, p_out.bound_box.ym, RoundMode.NEAREST) if self._tr_manager.get_next_track(gm_top_layer, _tidx0, 'sig_hs', 'sig_hs', 2) > _tidx1: # compute new _tidx0 and _tidx1 _, _locs = self._tr_manager.place_wires(gm_top_layer, ['sig_hs', 'sig_hs', 'sig_hs'], center_coord=(p_in.bound_box.ym + p_out.bound_box.ym) // 2) _tidx0, _tidx1 = _locs[0], _locs[-1] alternate_o = True else: alternate_o = False _coord0 = self.grid.track_to_coord(gm_top_layer, _tidx0) _coord1 = self.grid.track_to_coord(gm_top_layer, _tidx1) p_0, p_1 = p_in.lower, p_in.upper m_0, m_1 = m_in.lower, m_in.upper p_in = self.connect_via_stack(self._tr_manager, p_in, gm_top_layer, 'sig_hs', coord_list_p_override=[_coord0], alignment_o=1) p_in = self.extend_wires(p_in, lower=p_0, upper=p_1)[0] m_in = self.connect_via_stack(self._tr_manager, m_in, gm_top_layer, 'sig_hs', coord_list_p_override=[_coord0], alignment_o=-1) m_in = self.extend_wires(m_in, lower=m_0, upper=m_1)[0] p_out = self.connect_via_stack(self._tr_manager, p_out, gm_top_layer, 'sig_hs', coord_list_p_override=[_coord1], alternate_o=alternate_o, alignment_o=1) p_out = self.extend_wires(p_out, lower=p_0, upper=p_1)[0] m_out = self.connect_via_stack(self._tr_manager, m_out, gm_top_layer, 'sig_hs', coord_list_p_override=[_coord1], alternate_o=alternate_o, alignment_o=-1) m_out = self.extend_wires(m_out, lower=m_0, upper=m_1)[0] # now i_outp and p_in are on the same layer _conn_layer = gm_top_layer + 1 p_in = self.connect_via_stack(self._tr_manager, p_in, _conn_layer, 'sig_hs', mlm_dict={_conn_layer: MinLenMode.LOWER}) m_in = self.connect_via_stack(self._tr_manager, m_in, _conn_layer, 'sig_hs', mlm_dict={_conn_layer: MinLenMode.LOWER}) i_outp = self.connect_to_track_wires(i_outp, p_in) i_outm = self.connect_to_track_wires(i_outm, m_in) if gen_gm and gen_res: self.add_pin('i_outp', i_outp) self.add_pin('i_outm', i_outm) # routing from res_diff to inductors _tidx = self.grid.coord_to_track(_conn_layer + 1, p_out.bound_box.ym, RoundMode.GREATER) _coord = self.grid.track_to_coord(_conn_layer + 1, _tidx) p_out = self.connect_via_stack(self._tr_manager, p_out, _conn_layer + 1, 'sig_hs', coord_list_p_override=[_coord], mlm_dict={_conn_layer: MinLenMode.UPPER}) m_out = self.connect_via_stack(self._tr_manager, m_out, _conn_layer + 1, 'sig_hs', coord_list_p_override=[_coord], mlm_dict={_conn_layer: MinLenMode.UPPER}) ind_p: BBox = ind.get_pin('P1') ind_m: BBox = ind.get_pin('P3') ind_lp = self.grid.tech_info.get_lay_purp_list(ind_layer)[0] if _conn_layer + 1 == ind_layer: self.add_rect(ind_lp, BBox(ind_p.xl, p_out.bound_box.yl, ind_p.xh, ind_p.yh)) self.add_rect(ind_lp, BBox(ind_m.xl, m_out.bound_box.yl, ind_m.xh, ind_m.yh)) else: raise NotImplementedError # ind if gen_res: self.reexport(ind.get_port('P1'), net_name='ind_p') self.reexport(ind.get_port('P3'), net_name='ind_m') if gen_ind: if lay_mode is LayMode.TOP: for pin in ('P13_R', 'P2_R', 'P2'): # TODO: routing supplies self.reexport(ind.get_port(pin), net_name='VDD', connect=True) else: for pin in ('P13_R', 'P2_R', 'P2', 'P1', 'P3'): self.reexport(ind.get_port(pin)) # get schematic parameters self.sch_params = dict( gm=gm_master.sch_params, res=res_master.sch_params, ind=ind_sh_master.sch_params, lay_mode=lay_mode,
)