Source code for bag3_analog.layout.rdac

"""This module combines res_ladder and rdac_decoder."""

from typing import Mapping, Any, Optional, Type

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 TrackID, TrackManager, WDictType, SpDictType

from pybag.core import Transform, BBox
from pybag.enum import RoundMode, Direction, Orientation, PinMode, Orient2D

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

from .res.ladder import ResLadder
from .rdac_decoder import RDACDecoder
from ..schematic.rdac import bag3_analog__rdac


[docs]class RDAC(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) self._num_sel = -1 @property
[docs] def num_sel(self) -> int: return self._num_sel
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: return bag3_analog__rdac
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]: return dict( tr_widths='Track widths specifications for track manager', tr_spaces='Track spaces specifications for track manager', res_params='Parameters for res_ladder', dec_params='Parameters for rdac_decoder', num_dec='Number of decoders for one res_ladder', top_layer='Top metal layer for the RDAC, should be >= xxm_layer',
) @classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]: return dict(num_dec=1)
[docs] def draw_layout(self) -> None: # make master res_params: Mapping[str, Any] = self.params['res_params'] res_master = self.new_template(ArrayBaseWrapper, params=dict(cls_name=ResLadder.get_qualified_name(), params=res_params)) res_core: ResLadder = res_master.core num_dec: int = self.params['num_dec'] dec_params: Mapping[str, Any] = self.params['dec_params'] dec_master = self.new_template(GenericWrapper, params=dict(cls_name=RDACDecoder.get_qualified_name(), params=dec_params)) dec_core: RDACDecoder = dec_master.core num_sel_row: int = dec_params['num_sel_row'] num_sel_col: int = dec_params['num_sel_col'] self._num_sel = num_sel = num_sel_col + num_sel_row num_in = 1 << num_sel conn_layer = dec_core.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 vm_lp = self.grid.tech_info.get_lay_purp_list(vm_layer)[0] xm_layer = vm_layer + 1 ym_layer = xm_layer + 1 xxm_layer = ym_layer + 1 top_layer: int = self.params['top_layer'] assert top_layer >= xxm_layer, f'This generator expects top_layer={top_layer} is >= {xxm_layer}.' # --- Placement --- # res_w, res_h = res_master.bound_box.w, res_master.bound_box.h res_coord0 = res_core.core_coord0 dec_w, dec_h = dec_master.bound_box.w, dec_master.bound_box.h dec_coord0 = dec_core.pg_coord0 w_pitch, h_pitch = self.grid.get_size_pitch(xm_layer) h_pitch_2 = h_pitch // 2 # Allow half pitch, in accordance with ResLadder tracks tot_w = res_w + num_dec * dec_w assert res_coord0 < dec_coord0, 'These generator assumes RDACDecoder passgates start higher than ' \ 'ResLadder core.' if num_dec == 2: dec1_inst = self.add_instance(dec_master, xform=Transform(dx=dec_w, mode=Orientation.MY)) start_x = dec_w dec_list = [dec1_inst] elif num_dec == 1: dec1_inst = None start_x = 0 dec_list = [] else: raise ValueError(f'num_dec={num_dec} is not supported yet. Use 1 or 2.') dec0_inst = self.add_instance(dec_master, xform=Transform(dx=start_x + res_w)) dec_list.append(dec0_inst) _coord0 = min(dec0_inst.get_pin('VSS')[0].bound_box.ym, dec0_inst.get_pin('VDD')[0].bound_box.ym) res_inst = self.add_instance(res_master, xform=Transform(dx=start_x), commit=False) _coord1 = min(res_inst.get_pin('VSS')[0].bound_box.ym, res_inst.get_pin('VDD')[0].bound_box.ym) off_y = dec_coord0 - res_coord0 + (_coord0 - _coord1) off_y = -(- off_y // h_pitch_2) * h_pitch_2 res_inst.move_by(dy=off_y) res_inst.commit() tot_h = max(dec_h, res_h + off_y) self.set_size_from_bound_box(top_layer, BBox(0, 0, tot_w, tot_h), round_up=True) # --- Routing --- # # export select signals as WireArrays _sel: BBox = dec0_inst.get_pin('sel<0>') w_sel_vm = self.find_track_width(vm_layer, _sel.w) for idx in range(num_sel): _sel0: BBox = dec0_inst.get_pin(f'sel<{idx}>') _vm_tidx0 = self.grid.coord_to_track(vm_layer, _sel0.xm) _sel0_warr = self.add_wires(vm_layer, _vm_tidx0, lower=0, upper=_sel0.yh, width=w_sel_vm) if num_dec == 2: self.add_pin(f'sel0<{idx}>', _sel0_warr, mode=PinMode.LOWER) _sel1: BBox = dec1_inst.get_pin(f'sel<{idx}>') _vm_tidx1 = self.grid.coord_to_track(vm_layer, _sel1.xm) _sel1_warr = self.add_wires(vm_layer, _vm_tidx1, lower=0, upper=_sel1.yh, width=w_sel_vm) self.add_pin(f'sel1<{idx}>', _sel1_warr, mode=PinMode.LOWER) else: # num_dec == 1 self.add_pin(f'sel<{idx}>', _sel0_warr, mode=PinMode.LOWER) # export output as WireArray _out0 = dec0_inst.get_pin('out') if isinstance(_out0, BBox): w_out_ym = self.find_track_width(ym_layer, _out0.w) _ym_tidx0 = self.grid.coord_to_track(ym_layer, _out0.xm) _out0_warr = self.add_wires(ym_layer, _ym_tidx0, lower=_out0.yl, upper=self.bound_box.yh, width=w_out_ym) else: _out0_warr = self.extend_wires(_out0, upper=self.bound_box.yh) if num_dec == 2: self.add_pin('out0', _out0_warr, mode=PinMode.UPPER) _out1 = dec1_inst.get_pin('out') if isinstance(_out1, BBox): _ym_tidx1 = self.grid.coord_to_track(ym_layer, _out1.xm) _out1_warr = self.add_wires(ym_layer, _ym_tidx1, lower=_out1.yl, upper=self.bound_box.yh, width=w_out_ym) else: _out1_warr = self.extend_wires(_out1, upper=self.bound_box.yh) self.add_pin('out1', _out1_warr, mode=PinMode.UPPER) else: # num_dec == 1: self.add_pin('out', _out0_warr, mode=PinMode.UPPER) # res_ladder output to rdac_decoder input for idx in range(num_in): self.connect_bbox_to_track_wires(Direction.LOWER, vm_lp, dec0_inst.get_pin(f'in<{idx}>'), res_inst.get_pin(f'out<{idx}>')) if num_dec == 2: self.connect_bbox_to_track_wires(Direction.LOWER, vm_lp, dec1_inst.get_pin(f'in<{idx}>'), res_inst.get_pin(f'out<{idx}>')) # --- Supplies # get res supplies on xm_layer res_vss_xm = res_inst.get_all_port_pins('VSS', layer=xm_layer) res_vdd_xm = res_inst.get_all_port_pins('VDD', layer=xm_layer) # route res sup to ym_layer avoiding LR edge conflicts ym_lidx = self.grid.coord_to_track(ym_layer, res_vdd_xm[0].lower, RoundMode.GREATER) ym_ridx = self.grid.coord_to_track(ym_layer, res_vdd_xm[0].upper, RoundMode.LESS) num_ym = self._tr_manager.get_num_wires_between(ym_layer, 'sup', ym_lidx, 'sup', ym_ridx, 'sup') + 2 num_ym -= (1 - (num_ym & 1)) ym_locs = self._tr_manager.spread_wires(ym_layer, ['sup'] * num_ym, ym_lidx, ym_ridx, ('sup', 'sup')) _coords = [self.grid.track_to_coord(ym_layer, _tidx) for _tidx in ym_locs] vss_ym, vdd_ym = [], [] for sup_xm, sup_yym, cl in [(res_vss_xm, vss_ym, _coords[1::2]), (res_vdd_xm, vdd_ym, _coords[::2])]: for warr in sup_xm: for warr_single in warr.warr_iter(): sup_yym.append(self.connect_via_stack(self._tr_manager, warr_single, ym_layer, 'sup', coord_list_o_override=cl)) yh = self.bound_box.yh res_vss_ym = [self.connect_wires(vss_ym, lower=0, upper=yh)[0]] res_vdd_ym = [self.connect_wires(vdd_ym, lower=0, upper=yh)[0]] # Get dec supplies on xxm_layer dec_vss_xxm, dec_vdd_xxm = [], [] for inst in dec_list: dec_vss_xxm.extend(inst.get_all_port_pins('VSS', layer=xxm_layer)) dec_vdd_xxm.extend(inst.get_all_port_pins('VDD', layer=xxm_layer)) vss_top = self.connect_wires(dec_vss_xxm) vdd_top = self.connect_wires(dec_vdd_xxm) # Connect dec and res supplies vss_top = self.connect_to_track_wires(res_vss_ym, vss_top) vdd_top = self.connect_to_track_wires(res_vdd_ym, vdd_top) # Optional power straps for _layer in range(xxm_layer + 1, top_layer + 1): vdd_top, vss_top = self.do_power_fill(_layer, self._tr_manager, vdd_top, vss_top) for sup_top, sup_name in [(vdd_top, 'VDD'), (vss_top, 'VSS')]: sup = self.connect_wires(sup_top) if len(sup) == 1: self.add_pin(sup_name, sup[0]) else: self.add_pin(sup_name, sup_top, connect=True) self.warn(f'{sup_name} is a list of WireArrays with num = 1, so use get_all_port_pins()') # set schematic parameters self.sch_params = dict( res_params=res_master.sch_params, dec_params=dec_master.sch_params, num_dec=num_dec,
)