# SPDX-License-Identifier: BSD-3-Clause AND Apache-2.0
# Copyright 2018 Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 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.
"""This module contains layout generators for various memory elements."""
from typing import Any, Mapping, Optional, Type, Union
from pybag.enum import RoundMode, MinLenMode
from bag.util.immutable import Param
from bag.design.database import ModuleDB
from bag.design.module import Module
from bag.layout.template import TemplateDB
from bag.layout.routing.base import TrackID
from xbase.layout.enum import MOSWireType
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase
from .mux import Mux2to1Core
from .gates import InvTristateCore, NOR2Core, InvCore, PassGateCore, NAND2Core
# noinspection PyUnresolvedReferences
from ._flop_scan_rst import FlopScanRstlbTwoTile
from .util import RstType
from ...schematic.latch import bag3_digital__latch
from ...schematic.flop import bag3_digital__flop
from ...schematic.rst_flop import bag3_digital__rst_flop
from ...schematic.rst_latch import bag3_digital__rst_latch
[docs]class LatchCore(MOSBase):
"""A tristate inverter based latch."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
@property
[docs] def seg_in(self) -> int:
return self.sch_params['seg_dict']['tin']
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
return bag3_digital__latch
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments of output inverter.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
fanout_in='input stage fanout.',
fanout_kp='keeper stage fanout.',
vertical_sup='True to have supply unconnected on conn_layer; False by default',
vertical_flop='True to adjust vertical layer tracks so that latch can be tiled vertically to create flop; '
'False by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
fanout_in=4,
fanout_kp=8,
vertical_sup=False,
vertical_flop=False,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
hm_layer = pinfo.conn_layer + 1
vm_layer = hm_layer + 1
if pinfo.top_layer < vm_layer:
raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}')
seg: int = self.params['seg']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
fanout_in: float = self.params['fanout_in']
fanout_kp: float = self.params['fanout_kp']
vertical_sup: bool = self.params['vertical_sup']
vertical_flop: bool = self.params['vertical_flop']
# setup floorplan
self.draw_base(pinfo)
# compute track locations
tr_manager = pinfo.tr_manager
tr_w_v = tr_manager.get_width(vm_layer, 'sig')
if sig_locs is None:
sig_locs = {}
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
t0_in_tidx = sig_locs.get(key, self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=0))
t0_enb_tidx = sig_locs.get('pclkb', self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=1))
t0_en_tidx = sig_locs.get('nclk', self.get_track_index(ridx_n, MOSWireType.G,
wire_name='sig', wire_idx=0))
t1_en_tidx = sig_locs.get('nclkb', self.get_track_index(ridx_n, MOSWireType.G,
wire_name='sig', wire_idx=1))
t1_enb_tidx = sig_locs.get('pclk', t0_in_tidx)
t1_in_tidx = sig_locs.get('pout', t0_enb_tidx)
nd0_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
nd1_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
pd0_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
seg_t1 = max(1, int(round(seg / (2 * fanout_kp))) * 2)
seg_t0 = max(2 * seg_t1, max(2, int(round(seg / (2 * fanout_in))) * 2))
params = dict(pinfo=pinfo, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n, vertical_sup=vertical_sup)
inv_params = dict(**params, seg=seg, sig_locs={'nin': t0_en_tidx, 'pout': pd1_tidx, 'nout': nd1_tidx})
inv_master = self.new_template(InvCore, params=inv_params)
t0_params = dict(**params, seg=seg_t0, vertical_out=False,
sig_locs={'nin': t0_in_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx,
'nen': t0_en_tidx, 'pen': t0_enb_tidx})
t0_master = self.new_template(InvTristateCore, params=t0_params)
t1_params = dict(**params, seg=seg_t1, vertical_out=False,
sig_locs={'nin': t1_in_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx,
'nen': t1_en_tidx, 'pen': t1_enb_tidx})
t1_master = self.new_template(InvTristateCore, params=t1_params)
# set size
blk_sp = self.min_sep_col
t0_ncol = t0_master.num_cols
t1_ncol = t1_master.num_cols
inv_ncol = inv_master.num_cols
num_cols = t0_ncol + t1_ncol + inv_ncol + blk_sp * 2
self.set_mos_size(num_cols)
# add instances
t1_col = t0_ncol + blk_sp
inv_col = num_cols - inv_ncol
t0 = self.add_tile(t0_master, 0, 0)
t1 = self.add_tile(t1_master, 0, t1_col)
inv = self.add_tile(inv_master, 0, inv_col)
# connect/export VSS/VDD
vss_list, vdd_list = [], []
for inst in (t0, t1, inv):
vss_list.extend(inst.get_all_port_pins('VSS'))
vdd_list.extend(inst.get_all_port_pins('VDD'))
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# export input
self.reexport(t0.get_port('nin'), net_name='nin', hide=True)
self.reexport(t0.get_port('pin'), net_name='pin', hide=True)
self.reexport(t0.get_port('in'))
# connect output
out = inv.get_pin('out')
in2 = t1.get_pin('nin')
self.connect_to_track_wires(in2, out)
self.add_pin('out', out)
self.add_pin('nout', in2, hide=True)
self.add_pin('pout', in2, hide=True)
# connect middle node
if vertical_flop:
mid_tid = tr_manager.get_next_track_obj(out, 'sig', 'sig', -1)
else:
col = inv_col - max(1, blk_sp // 2)
mid_tid = TrackID(vm_layer, pinfo.get_source_track(col), width=tr_w_v)
warrs = [t0.get_pin('pout'), t0.get_pin('nout'), t1.get_pin('pout'), t1.get_pin('nout'),
inv.get_pin('nin')]
hm_warrs = []
self.connect_to_tracks(warrs, mid_tid, ret_wire_list=hm_warrs)
self.add_pin('outb', inv.get_pin('in'), hide=True)
self.add_pin('noutb', inv.get_pin('nin'), hide=True)
self.add_pin('poutb', inv.get_pin('nin'), hide=True)
# connect clocks
if vertical_flop:
r_tidx = tr_manager.get_next_track(vm_layer, mid_tid.base_index, 'sig', 'sig', -2)
r_off = self.bound_box.xh - self.grid.track_to_coord(vm_layer, r_tidx)
l_off = t0_ncol * self.sd_pitch
if r_off > l_off:
raise ValueError('Not possible to route on vertical tracks.')
_off = (l_off + r_off) // 2
clk_tidx = self.grid.coord_to_track(vm_layer, self.bound_box.xh - _off, RoundMode.NEAREST)
clkb_tidx = self.grid.coord_to_track(vm_layer, _off, RoundMode.NEAREST)
else:
clk_tidx = sig_locs.get('clk', pinfo.get_source_track(t1_col + 1))
clkb_tidx = sig_locs.get('clkb', pinfo.get_source_track(t1_col - blk_sp - 1))
clk_tid = TrackID(vm_layer, clk_tidx, width=tr_w_v)
clkb_tid = TrackID(vm_layer, clkb_tidx, width=tr_w_v)
t0_en = t0.get_pin('en')
t1_en = t1.get_pin('en')
t1_enb = t1.get_pin('enb')
t0_enb = t0.get_pin('enb')
clk = self.connect_to_tracks([t0_en, t1_enb], clk_tid)
clkb = self.connect_to_tracks([t0_enb, t1_en], clkb_tid)
self.add_pin('clk', clk)
self.add_pin('clkb', clkb)
self.add_pin('nclk', t0_en, hide=True)
self.add_pin('pclk', t1_enb, hide=True)
self.add_pin('nclkb', t1_en, hide=True)
self.add_pin('pclkb', t0_enb, hide=True)
# extend t1_en to avoid corner-to-corner spacing error for hm_layer
self.extend_wires(t1_en, upper=hm_warrs[0].upper)
# set properties
self._sch_params = dict(
lch=self.place_info.lch,
w_n=self.place_info.get_row_place_info(ridx_n).row_info.width if w_n == 0 else w_n,
w_p=self.place_info.get_row_place_info(ridx_p).row_info.width if w_p == 0 else w_p,
th_n=self.place_info.get_row_place_info(ridx_n).row_info.threshold,
th_p=self.place_info.get_row_place_info(ridx_p).row_info.threshold,
seg_dict=dict(tin=seg_t0, tfb=seg_t1, buf=seg),
)
[docs]class FlopCore(MOSBase):
"""A tristate inverter based flip-flop."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._cntr_col_clk = None
@property
[docs] def seg_in(self):
return self.sch_params['seg_m']['in']
@property
[docs] def cntr_col_clk(self):
return self._cntr_col_clk
[docs] def get_schematic_class_inst(self) -> Optional[Type[Module]]:
rst: bool = self.params['resetable']
scan: bool = self.params['scanable']
if scan:
raise ValueError('See Developer')
if rst:
return bag3_digital__rst_flop
return bag3_digital__flop
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments of output inverter.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
fanout_in='latch input stage fanout.',
fanout_kp='latch keeper stage fanout.',
fanout_lat='fanout between latches.',
fanout_mux='fanout of scan mux, if present.',
seg_ck='number of segments for clock inverter. 0 to disable.',
seg_mux='Dictionary of segments for scan mux, if scanable',
resetable='True if flop is resetable, default is False',
rst_type='SET or RESET; RESET by default',
scanable='True if flop needs to have scanability',
extra_sp='This parameter is added to the min value of one of the separations '
'(mostly used to make power vertical stripes aligned)',
vertical_sup='True to have supply unconnected on conn_layer; False by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
fanout_in=4,
fanout_kp=8,
fanout_lat=4,
fanout_mux=4,
seg_ck=0,
seg_mux=None,
resetable=False,
rst_type=RstType.RESET,
scanable=False,
extra_sp=0,
vertical_sup=False,
)
[docs] def draw_layout(self):
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
seg: int = self.params['seg']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
fanout_in: float = self.params['fanout_in']
fanout_kp: float = self.params['fanout_kp']
fanout_lat: float = self.params['fanout_lat']
fanout_mux: float = self.params['fanout_mux']
seg_ck: int = self.params['seg_ck']
seg_mux: Optional[Mapping[str, int]] = self.params['seg_mux']
rst: bool = self.params['resetable']
rst_type: Union[str, RstType] = self.params['rst_type']
if isinstance(rst_type, str):
rst_type = RstType[rst_type]
scan: bool = self.params['scanable']
extra_sp: int = self.params['extra_sp']
vertical_sup: bool = self.params['vertical_sup']
# setup floorplan
self.draw_base(pinfo)
# compute track locations
if sig_locs is None:
sig_locs = {}
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
in_tidx = sig_locs.get(key, self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=0))
pclkb_tidx = sig_locs.get('pclkb', self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=0))
nclk_idx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
nclkb_idx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
pclk_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
clk_idx = sig_locs.get('clk', None)
clkb_idx = sig_locs.get('clkb', None)
# make masters
lat_params = dict(pinfo=pinfo, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n, fanout_in=fanout_in,
fanout_kp=fanout_kp, vertical_sup=vertical_sup)
if rst:
lat_params['rst_type'] = rst_type
s_params = dict(**lat_params, seg=seg, sig_locs={'nclk': nclk_idx, 'nclkb': nclkb_idx, 'pclk': pclk_tidx,
'nin': pclk_tidx, 'pclkb': pclkb_tidx,
'pout': sig_locs.get('pout', pclkb_tidx)})
s_master = self.new_template(RstLatchCore if rst else LatchCore, params=s_params)
seg_m = max(2, int(round(s_master.seg_in / (2 * fanout_lat))) * 2)
m_sig_locs = {'nclk': nclkb_idx, 'nclkb': nclk_idx, 'pclk': pclkb_tidx, 'nin': in_tidx, 'pclkb': pclk_tidx}
if clk_idx is not None:
m_sig_locs['clkb'] = clk_idx
if clkb_idx is not None:
m_sig_locs['clk'] = clkb_idx
m_params = dict(**lat_params, seg=seg_m, sig_locs=m_sig_locs)
m_master = self.new_template(RstLatchCore if rst else LatchCore, params=m_params)
cur_col = 0
blk_sp = self.min_sep_col
pd0_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
nd0_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pg0_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0)
pg1_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
inst_list = []
mux_inst = None
mux_master = None
if scan:
if seg_mux is None:
raise ValueError('Please specify segments for scan mux.')
mux_params = dict(pinfo=pinfo, seg=seg_mux['seg'], w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sel_seg=seg_mux['sel_seg'], fout=fanout_mux,
sig_locs={'pselb': pd1_tidx, 'pin1': pg0_tidx, 'penb': pg1_tidx},
vertical_out=False)
mux_master = self.new_template(Mux2to1Core, params=mux_params)
mux_inst = self.add_tile(mux_master, 0, cur_col)
mux_ncol = mux_master.num_cols
cur_col += mux_ncol + blk_sp
inst_list.append(mux_inst)
m_ncol = m_master.num_cols
s_ncol = s_master.num_cols
m_inv_sp = blk_sp if rst else 0
inv_master = None
if seg_ck > 0:
params = dict(pinfo=pinfo, seg=seg_ck, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs={'nin': nclk_idx, 'pout': pd0_tidx,
'nout': nd0_tidx}, vertical_sup=vertical_sup)
inv_master = self.new_template(InvCore, params=params)
ncol = cur_col + m_ncol + s_ncol + blk_sp + inv_master.num_cols + m_inv_sp
scol = cur_col + m_ncol + inv_master.num_cols + blk_sp + m_inv_sp + extra_sp
b_inst = self.add_tile(inv_master, 0, cur_col + m_ncol + m_inv_sp)
self._cntr_col_clk = scol - (blk_sp + extra_sp) // 2
else:
ncol = cur_col + m_ncol + s_ncol + blk_sp
scol = cur_col + m_ncol + blk_sp + extra_sp
self._cntr_col_clk = scol - (blk_sp + extra_sp) // 2
b_inst = None
# set size
self.set_mos_size(ncol)
# add instances
m_inst = self.add_tile(m_master, 0, cur_col)
s_inst = self.add_tile(s_master, 0, scol)
inst_list.append(m_inst)
inst_list.append(s_inst)
# connect/export VSS/VDD
vss_list, vdd_list = [], []
for inst in inst_list:
vss_list.extend(inst.get_all_port_pins('VSS'))
vdd_list.extend(inst.get_all_port_pins('VDD'))
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# connect intermediate node
self.connect_wires([s_inst.get_pin('nin'), m_inst.get_pin('nout')])
# connect clocks
pclkb = self.connect_wires([s_inst.get_pin('pclkb'), m_inst.get_pin('pclk')])
if b_inst is None:
self.connect_wires([s_inst.get_pin('nclk'), m_inst.get_pin('nclkb')])
self.reexport(m_inst.get_port('clk'), net_name='clkb')
self.reexport(m_inst.get_port('nclk'), net_name='nclkb')
self.reexport(s_inst.get_port('clkb'), net_name='clkb_s', hide=True)
self.reexport(s_inst.get_port('nclkb'), net_name='nclkb_s')
else:
self.connect_wires([s_inst.get_pin('nclk'), m_inst.get_pin('nclkb'),
b_inst.get_pin('nin')])
self.connect_to_track_wires(pclkb, b_inst.get_pin('out'))
# connect rst if rst is True
if rst:
if rst_type is RstType.RESET:
rst_name = 'rst'
elif rst_type is RstType.SET:
rst_name = 'setb'
else:
raise ValueError(f'Unknown rst_type={rst_type}. Use SET or RESET.')
rst_warr = self.connect_wires([s_inst.get_pin(f'n{rst_name}'), m_inst.get_pin(f'n{rst_name}')])
self.add_pin(f'n{rst_name}', rst_warr, hide=True)
self.add_pin(f'p{rst_name}', rst_warr, hide=True)
self.add_pin(rst_name, [s_inst.get_pin(rst_name), m_inst.get_pin(rst_name)], connect=True)
# connect mux output to flop input if scan is true
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
if scan:
flop_in = m_inst.get_pin('nin')
mux_out_vm_tidx = self.grid.coord_to_track(vm_layer, flop_in.lower,
mode=RoundMode.NEAREST)
mux_out = [mux_inst.get_pin('pout'), mux_inst.get_pin('nout'), flop_in]
flop_in_vm = self.connect_to_tracks(mux_out, TrackID(vm_layer, mux_out_vm_tidx))
self.add_pin('flop_in', flop_in_vm, hide=True)
# add pins
# NOTE: use reexport so hidden pins propagate correctly
if scan:
self.reexport(mux_inst.get_port('in<0>'), net_name='in', hide=False)
self.reexport(mux_inst.get_port('in<1>'), net_name='scan_in', hide=False)
self.reexport(mux_inst.get_port('sel'), net_name='scan_en', hide=False)
else:
self.reexport(m_inst.get_port('in'))
self.reexport(m_inst.get_port('nin'))
self.reexport(m_inst.get_port('pin'))
self.reexport(s_inst.get_port('out'))
self.reexport(s_inst.get_port('nout'))
self.reexport(s_inst.get_port('pout'))
self.reexport(m_inst.get_port('clkb'), net_name='clk')
self.reexport(m_inst.get_port('nclkb'), net_name='nclk')
self.reexport(m_inst.get_port('pclkb'), net_name='pclk')
self.reexport(s_inst.get_port('clk'), net_name='clk_s', hide=True)
self.reexport(s_inst.get_port('pclk'), net_name='pclk_s')
self.reexport(s_inst.get_port('outb'), net_name='outb')
self.reexport(s_inst.get_port('noutb'), net_name='noutb')
self.reexport(s_inst.get_port('poutb'), net_name='poutb')
if rst:
self.reexport(m_inst.get_port('mid_vm'), net_name='mid_vm_m', hide=True)
self.reexport(s_inst.get_port('mid_vm'), net_name='mid_vm_s', hide=True)
# set properties
if rst:
self.sch_params = dict(
m_params=m_master.sch_params,
s_params=s_master.sch_params,
inv_params=inv_master.sch_params if inv_master else None,
rst_type=rst_type,
)
else:
self.sch_params = dict(
lch=self.place_info.lch,
w_n=self.place_info.get_row_place_info(ridx_n).row_info.width if w_n == 0 else w_n,
w_p=self.place_info.get_row_place_info(ridx_p).row_info.width if w_p == 0 else w_p,
th_n=self.place_info.get_row_place_info(ridx_n).row_info.threshold,
th_p=self.place_info.get_row_place_info(ridx_p).row_info.threshold,
seg_m=m_master.sch_params['seg_dict'],
seg_s=s_master.sch_params['seg_dict'],
seg_ck=seg_ck,
)
if scan:
self.sch_params = dict(
rst_flop_params=self.sch_params,
mux_params=mux_master.sch_params,
)
[docs]class RstLatchCore(MOSBase):
"""A tristate inverter based latch with NAND reset."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._seg_in = None
@property
[docs] def seg_in(self) -> int:
return self._seg_in
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
return bag3_digital__rst_latch
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments of output NOR.',
seg_dict='Dictionary of number of segments',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
fanout_in='input stage fanout.',
fanout_kp='keeper stage fanout.',
vertical_clk='True to have vertical clk and clkb',
rst_type='SET or RESET; RESET by default',
vertical_sup='True to have supply unconnected on conn_layer; False by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
fanout_in=4,
fanout_kp=8,
seg_dict=None,
seg=1,
vertical_clk=True,
rst_type=RstType.RESET,
vertical_sup=False,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
hm_layer = pinfo.conn_layer + 1
vm_layer = hm_layer + 1
if pinfo.top_layer < vm_layer:
raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}')
# setup floorplan
self.draw_base(pinfo)
seg: int = self.params['seg']
seg_dict: Optional[Mapping[str, int]] = self.params['seg_dict']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
fanout_in: float = self.params['fanout_in']
fanout_kp: float = self.params['fanout_kp']
vertical_clk: bool = self.params['vertical_clk']
vertical_sup: bool = self.params['vertical_sup']
rst_type: Union[str, RstType] = self.params['rst_type']
if isinstance(rst_type, str):
rst_type = RstType[rst_type]
# compute track locations and create masters
tr_manager = pinfo.tr_manager
tr_w_v = tr_manager.get_width(vm_layer, 'sig')
if sig_locs is None:
sig_locs = {}
ng0 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
ng1 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
pg0 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0)
pg1 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
pg2 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=2)
nd0 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
nd1 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
pd0 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
if seg_dict is None:
seg_t1 = max(1, int(round(seg / (2 * fanout_kp))) * 2)
seg_t0 = self._seg_in = max(2 * seg_t1, max(2, int(round(seg / (2 * fanout_in))) * 2))
else:
seg = seg_dict['nor']
seg_t1 = seg_dict['keep']
seg_t0 = seg_dict['in']
nor_sig_locs = dict(
nin0=sig_locs.get('rst', pg2),
nin1=sig_locs.get('nclk', ng0),
pout=pd1,
nout=nd1,
)
params = dict(pinfo=pinfo, seg=seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
sig_locs=nor_sig_locs, vertical_sup=vertical_sup)
if rst_type is RstType.RESET:
nor_master = self.new_template(NOR2Core, params=params)
rst_name = 'rst'
elif rst_type is RstType.SET:
nor_master = self.new_template(NAND2Core, params=params)
rst_name = 'setb'
else:
raise ValueError(f'Unknown rst_type={rst_type}. Use RESET (default) or SET.')
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
t0_sig_locs = dict(
nin=sig_locs.get(key, pg0),
pout=pd0,
nout=nd0,
nen=sig_locs.get('nclk', ng0),
pen=sig_locs.get('pclkb', pg1),
)
params = dict(pinfo=pinfo, seg=seg_t0, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=t0_sig_locs, vertical_sup=vertical_sup)
t0_master = self.new_template(InvTristateCore, params=params)
t1_sig_locs = dict(
nin=sig_locs.get('pout', pg1),
pout=pd0,
nout=nd0,
nen=sig_locs.get('nclkb', ng1),
pen=sig_locs.get('pclk', pg0),
)
params = dict(pinfo=pinfo, seg=seg_t1, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=t1_sig_locs, vertical_sup=vertical_sup)
t1_master = self.new_template(InvTristateCore, params=params)
# set size
blk_sp = max(self.min_sep_col, self.get_hm_sp_le_sep_col())
t0_ncol = t0_master.num_cols
t1_ncol = t1_master.num_cols
nor_ncol = nor_master.num_cols
num_cols = t0_ncol + t1_ncol + nor_ncol + blk_sp * 2
self.set_mos_size(num_cols)
# add instances
t1_col = t0_ncol + blk_sp
nor_col = num_cols - nor_ncol
t0 = self.add_tile(t0_master, 0, 0)
t1 = self.add_tile(t1_master, 0, t1_col)
nor = self.add_tile(nor_master, 0, nor_col)
# routing
# connect/export VSS/VDD
vss_list, vdd_list = [], []
for inst in (t0, t1, nor):
vss_list.extend(inst.get_all_port_pins('VSS'))
vdd_list.extend(inst.get_all_port_pins('VDD'))
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# export input
self.reexport(t0.get_port('nin'), net_name='nin', hide=True)
self.reexport(t0.get_port('pin'), net_name='pin', hide=True)
self.reexport(t0.get_port('in'))
# connect output
out = nor.get_pin('out')
in2 = t1.get_pin('nin')
self.connect_to_track_wires(in2, out)
self.add_pin('out', out)
self.add_pin('nout', in2, hide=True)
self.add_pin('pout', in2, hide=True)
# connect middle node
mid_coord = (t1.bound_box.xh + nor.bound_box.xl) // 2
mid_tidx = self.grid.coord_to_track(vm_layer, mid_coord, RoundMode.NEAREST)
mid_tid = TrackID(vm_layer, mid_tidx, width=tr_w_v)
if out.layer_id == vm_layer:
next_tidx = tr_manager.get_next_track(vm_layer, mid_tidx, 'sig', 'sig', up=True)
if next_tidx >= out.track_id.base_index:
raise ValueError('oops!')
warrs = [t0.get_pin('pout'), t0.get_pin('nout'), t1.get_pin('pout'), t1.get_pin('nout'),
nor.get_pin('nin<1>')]
mid_vm_warr = self.connect_to_tracks(warrs, mid_tid)
# connect clocks
clk_tidx = sig_locs.get('clk', pinfo.get_source_track(t1_col + 1))
clkb_tidx = sig_locs.get('clkb', pinfo.get_source_track(t1_col - blk_sp - 1))
clk_tid = TrackID(vm_layer, clk_tidx, width=tr_w_v)
clkb_tid = TrackID(vm_layer, clkb_tidx, width=tr_w_v)
t0_en = t0.get_pin('en')
t1_en = t1.get_pin('en')
t1_enb = t1.get_pin('enb')
t0_enb = t0.get_pin('enb')
clk_hm = [t0_en, t1_enb]
clkb_hm = [t0_enb, t1_en]
if vertical_clk:
clk = self.connect_to_tracks(clk_hm, clk_tid)
clkb = self.connect_to_tracks(clkb_hm, clkb_tid)
self.add_pin('clk', clk)
self.add_pin('clkb', clkb)
self.add_pin('outb', [nor.get_pin('in<1>'), mid_vm_warr], hide=True)
self.add_pin('noutb', nor.get_pin('nin<1>'), hide=True)
self.add_pin('poutb', nor.get_pin('nin<1>'), hide=True)
self.add_pin(rst_name, nor.get_pin('in<0>'))
self.add_pin(f'n{rst_name}', nor.get_pin('nin<0>'), hide=True)
self.add_pin(f'p{rst_name}', nor.get_pin('nin<0>'), hide=True)
self.add_pin('mid_vm', mid_vm_warr, hide=True)
self.add_pin('nclk', t0_en, label='clk:', hide=vertical_clk)
self.add_pin('pclk', t1_enb, label='clk:', hide=vertical_clk)
self.add_pin('nclkb', t1_en, label='clkb:', hide=vertical_clk)
self.add_pin('pclkb', t0_enb, label='clkb:', hide=vertical_clk)
# set properties
self.sch_params = dict(
tin=t0_master.sch_params,
tfb=t1_master.sch_params,
nor=nor_master.sch_params,
rst_type=rst_type,
)
[docs]class RstLatchCore2Row(MOSBase):
"""A tristate inverter based latch with NAND reset and optional scanability, 2 row layout."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._seg_in = None
@property
[docs] def seg_in(self) -> int:
return self._seg_in
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
# noinspection PyTypeChecker
return ModuleDB.get_schematic_class('bag3_digital', 'scan_rst_latch')
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments of output NOR.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
fanout_in='input stage fanout.',
fanout_kp='keeper stage fanout.',
scan='True to enable scanability.',
dual_output='True to show out and outb',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
fanout_in=4,
fanout_kp=8,
scan=True,
dual_output=True,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
hm_layer = pinfo.conn_layer + 1
vm_layer = hm_layer + 1
if pinfo.top_layer < vm_layer:
raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}')
# setup floorplan
self.draw_base(pinfo)
seg: int = self.params['seg']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
fanout_in: float = self.params['fanout_in']
fanout_kp: float = self.params['fanout_kp']
scan: bool = self.params['scan']
dual_output: bool = self.params['dual_output']
# compute track locations and create masters
tr_manager = pinfo.tr_manager
tr_w_v = tr_manager.get_width(vm_layer, 'sig')
if sig_locs is None:
sig_locs = {}
ng0 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
ng1 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
ng2 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2)
pg0 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0)
pg1 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
pg2 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=2)
nd0 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
nd1 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
pd0 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
seg_t1 = max(1, int(round(seg / (2 * fanout_kp))) * 2)
seg_tg = max(2 * seg_t1, max(2, int(round(seg / (2 * fanout_in))) * 2))
seg_t0 = self._seg_in = max(2, int(round(seg_tg / (2 * fanout_in))) * 2)
seg_inv = seg_t0
nor_sig_locs = dict(
nin0=sig_locs.get('rst', ng2),
nin1=sig_locs.get('nclk', pg0 if scan else ng0),
pout=pd1,
nout=nd1,
)
params = dict(pinfo=pinfo, seg=seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
sig_locs=nor_sig_locs)
nor_master = self.new_template(NOR2Core, params=params)
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
t0_sig_locs = dict(
nin=sig_locs.get(key, pg2),
pout=pd0,
nout=nd0,
nen=sig_locs.get('nclk_in', ng1),
pen=sig_locs.get('pclkb_in', pg1),
)
params = dict(pinfo=pinfo, seg=seg_t0, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=t0_sig_locs)
t0_in_master = self.new_template(InvTristateCore, params=params)
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
t0_sig_locs = dict(
nin=sig_locs.get(key, pg2),
pout=pd0,
nout=nd1,
nen=sig_locs.get('nclk_scan', ng1),
pen=sig_locs.get('pclkb_scan', pg1),
)
params = dict(pinfo=pinfo, seg=seg_t0, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=t0_sig_locs)
t0_scan_master = self.new_template(InvTristateCore, params=params)
t1_sig_locs = dict(
nin=sig_locs.get('pout', pg1),
pout=pd0,
nout=nd1,
nen=sig_locs.get('nclkb', ng2),
pen=sig_locs.get('pclk', pg0),
)
params = dict(pinfo=pinfo, seg=seg_t1, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=t1_sig_locs)
t1_master = self.new_template(InvTristateCore, params=params)
tg_sig_locs = dict(
ns=sig_locs.get('pout', nd0),
)
params = dict(pinfo=pinfo, seg=seg_tg, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=True, sig_locs=tg_sig_locs)
tg_in_master = self.new_template(PassGateCore, params=params)
tg_sig_locs = dict(
ns=sig_locs.get('pout', ng1),
)
params = dict(pinfo=pinfo, seg=seg_tg, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=True, sig_locs=tg_sig_locs)
tg_scan_master = self.new_template(PassGateCore, params=params)
inv_sig_locs = dict()
params = dict(pinfo=pinfo, seg=seg_inv, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=inv_sig_locs)
inv_master = self.new_template(InvCore, params=params)
# set size
blk_sp = max(self.min_sep_col, self.get_hm_sp_le_sep_col())
t0_ncol = t0_in_master.num_cols
t1_ncol = t1_master.num_cols
nor_ncol = nor_master.num_cols
tg_ncol = tg_in_master.num_cols
inv_ncol = inv_master.num_cols
num_cols = t0_ncol + blk_sp + max(t1_ncol, nor_ncol)
if scan:
num_cols += inv_ncol + blk_sp + tg_ncol + blk_sp
self.set_mos_size(num_cols, 2)
# --- Placement --- #
inv_col = 0
t0_col = inv_col + inv_ncol + blk_sp if scan else 0
tg_col = t0_col + t0_ncol + blk_sp
nor_col = tg_col + tg_ncol + blk_sp if scan else tg_col
# add instances
inv = self.add_tile(inv_master, 0, inv_col) if scan else None
t0_in = self.add_tile(t0_in_master, 1, t0_col)
t0_scan = self.add_tile(t0_scan_master, 0, t0_col) if scan else None
tg_in = self.add_tile(tg_in_master, 1, tg_col) if scan else None
tg_scan = self.add_tile(tg_scan_master, 0, tg_col) if scan else None
nor = self.add_tile(nor_master, 1, nor_col)
t1 = self.add_tile(t1_master, 0, nor_col + t1_ncol, flip_lr=True)
# --- Routing --- #
# connect/export VSS/VDD
vss_list, vdd_list = [], []
inst_list = [t0_in, nor, t1]
if scan:
inst_list += [inv, t0_scan, tg_in, tg_scan]
for inst in inst_list:
vss_list.append(inst.get_pin('VSS'))
vdd_list.append(inst.get_pin('VDD'))
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# export input
self.reexport(t0_in.get_port('nin'), net_name='in', hide=False)
# export rst
self.reexport(nor.get_port('nin<0>'), net_name='rst', hide=False)
# connect clk and clkb
t0_in_nout = t0_in.get_pin('nout')
mid_tidx = self.grid.coord_to_track(vm_layer, t0_in_nout.middle, mode=RoundMode.NEAREST)
clk_vm_tidx = tr_manager.get_next_track(vm_layer, mid_tidx, 'sig', 'sig', up=False)
clkb_vm_tidx = tr_manager.get_next_track(vm_layer, mid_tidx, 'sig', 'sig', up=True)
inb_vm_tidx = tr_manager.get_next_track(vm_layer, clkb_vm_tidx, 'sig', 'sig', up=True)
clk_in = t0_in.get_pin('en')
clkb_in = t0_in.get_pin('enb')
self.add_pin('clk', clk_in)
self.add_pin('clkb', clkb_in)
clk_hm_warrs = [clk_in, t1.get_pin('enb')]
clkb_hm_warrs = [clkb_in, t1.get_pin('en')]
# connect output of scan tristate inverter to input of scan pass gate
inb_warrs = [t0_in.get_pin(name) for name in ['pout', 'nout']]
inb_warrs += [tg_in.get_pin('s')] if scan else [nor.get_pin('nin<1>'), t1.get_pin('nout'),
t1.get_pin('pout')]
self.connect_to_tracks(inb_warrs, TrackID(vm_layer, inb_vm_tidx, width=tr_w_v))
# export scan input
if scan:
self.reexport(t0_scan.get_port('nin'), net_name='scan_in', hide=False)
# connect scan_en inverter
tg_out_tidx = tg_scan.get_pin('d').track_id.base_index
scan_en_tidx = tr_manager.get_next_track(vm_layer, tg_out_tidx, 'sig', 'sig', up=False)
scan_enb_tidx = tr_manager.get_next_track(vm_layer, tg_out_tidx, 'sig', 'sig', up=True)
scan_en = inv.get_pin('in')
self.add_pin('scan_en', scan_en)
self.connect_to_tracks([scan_en, tg_scan.get_pin('en'), tg_in.get_pin('enb')],
TrackID(vm_layer, scan_en_tidx, width=tr_w_v))
self.connect_to_tracks([inv.get_pin('pout'), inv.get_pin('nout'),
tg_scan.get_pin('enb'), tg_in.get_pin('en')],
TrackID(vm_layer, scan_enb_tidx, width=tr_w_v))
# clk and clkb of scan tri-state
clk_hm_warrs.append(t0_scan.get_pin('en'))
clkb_hm_warrs.append(t0_scan.get_pin('enb'))
# connect output of scan tristate inverter to input of scan pass gate
self.connect_to_tracks([t0_scan.get_pin('nout'), t0_scan.get_pin('pout'),
tg_scan.get_pin('s')],
TrackID(vm_layer, inb_vm_tidx, width=tr_w_v))
# connect passgate outputs together to in1 of nor and out of feedback tristate
self.connect_to_track_wires([nor.get_pin('nin<1>'), t1.get_pin('pout'),
t1.get_pin('nout')],
self.connect_wires([tg_scan.get_pin('d'),
tg_in.get_pin('d')]))
# finish clk and clkb connection
clk_vm = self.connect_to_tracks(clk_hm_warrs, TrackID(vm_layer, clk_vm_tidx, width=tr_w_v))
clkb_vm = self.connect_to_tracks(clkb_hm_warrs, TrackID(vm_layer, clkb_vm_tidx,
width=tr_w_v))
self.add_pin('clk_vm', clk_vm, hide=True)
self.add_pin('clkb_vm', clkb_vm, hide=True)
# connect nor output to feedback tristate input
out = self.connect_to_track_wires(t1.get_pin('nin'), nor.get_pin('out'))
self.add_pin('out', out)
self.reexport(nor.get_port('pout'))
self.reexport(nor.get_port('nout'))
self.reexport(nor.get_port('nin<1>'), net_name='outb', hide=not dual_output)
# set properties
sch_params = dict(
tin=t0_in_master.sch_params,
tfb=t1_master.sch_params,
nor=nor_master.sch_params,
scan=scan,
dual_output=dual_output,
)
if scan:
sch_params.update(dict(
pg=tg_in_master.sch_params,
inv=inv_master.sch_params,
))
self.sch_params = sch_params
[docs]class FlopCore2Row(MOSBase):
"""A tristate inverter based flip-flop in 2 rows."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._cntr_col_clk = None
@property
[docs] def seg_in(self):
return self.sch_params['seg_m']['in']
@property
[docs] def cntr_col_clk(self):
return self._cntr_col_clk
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
# noinspection PyTypeChecker
return ModuleDB.get_schematic_class('bag3_digital', 'scan_rst_flop')
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments of output inverter.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
fanout_in='latch input stage fanout.',
fanout_kp='latch keeper stage fanout.',
fanout_lat='fanout between latches.',
seg_ck='number of segments for clock inverter. 0 to disable.',
resetable='True if flop is resetable, default is False',
scan='True if flop needs to have scanability',
extra_sp='This parameter is added to the min value of one of the separations '
'(mostly used to make power vertical stripes aligned)'
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
fanout_in=4,
fanout_kp=8,
fanout_lat=4,
fanout_mux=4,
seg_ck=0,
seg_mux=None,
resetable=False,
scan=False,
extra_sp=0,
)
[docs] def draw_layout(self):
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
seg: int = self.params['seg']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
fanout_in: float = self.params['fanout_in']
fanout_kp: float = self.params['fanout_kp']
fanout_lat: float = self.params['fanout_lat']
seg_ck: int = self.params['seg_ck']
rst: bool = self.params['resetable']
scan: bool = self.params['scan']
extra_sp: int = self.params['extra_sp']
if rst is False:
raise NotImplementedError('2 row Latch not implemented yet')
# setup floorplan
self.draw_base(pinfo)
# compute track locations
if sig_locs is None:
sig_locs = {}
# key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
# in_idx = sig_locs.get(key, self.get_track_index(ridx_p, MOSWireType.G,
# wire_name='sig', wire_idx=0))
pclkb_idx = sig_locs.get('pclkb', self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=0))
# nclk_idx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
# nclkb_idx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
pclk_idx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
# clk_idx = sig_locs.get('clk', None)
# clkb_idx = sig_locs.get('clkb', None)
# make masters
s_sig_locs = {
'pclkb_in': pclkb_idx,
}
lat_params = dict(pinfo=pinfo, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
fanout_in=fanout_in, fanout_kp=fanout_kp)
s_params = dict(**lat_params, seg=seg, scan=False, sig_locs=s_sig_locs)
s_master = self.new_template(RstLatchCore2Row, params=s_params)
seg_m = max(2, int(round(s_master.seg_in / (2 * fanout_lat))) * 2)
m_params = dict(**lat_params, seg=seg_m, scan=scan, dual_output=False)
m_master = self.new_template(RstLatchCore2Row, params=m_params)
cur_col = 0
blk_sp = self.min_sep_col
pd0_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
# pd1_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
nd0_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
# pg0_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0)
# pg1_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
inst_list = []
m_ncol = m_master.num_cols
s_ncol = s_master.num_cols
m_inv_sp = blk_sp if rst else 0
inv_master = None
if seg_ck > 0:
params = dict(pinfo=pinfo, seg=seg_ck, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs={'nin': pclk_idx, 'pout': pd0_tidx,
'nout': nd0_tidx})
inv_master = self.new_template(InvCore, params=params)
ncol = cur_col + m_ncol + s_ncol + blk_sp + inv_master.num_cols + m_inv_sp
scol = cur_col + m_ncol + inv_master.num_cols + blk_sp + m_inv_sp + extra_sp
b_inst = self.add_tile(inv_master, 1, cur_col + m_ncol + m_inv_sp)
self._cntr_col_clk = scol - (blk_sp + extra_sp) // 2
else:
ncol = cur_col + m_ncol + s_ncol + blk_sp
scol = cur_col + m_ncol + blk_sp + extra_sp
self._cntr_col_clk = scol - (blk_sp + extra_sp) // 2
b_inst = None
# set size
self.set_mos_size(ncol)
# add instances
m_inst = self.add_tile(m_master, 0, cur_col)
s_inst = self.add_tile(s_master, 0, scol)
inst_list.append(m_inst)
inst_list.append(s_inst)
# connect/export VSS/VDD
vss_list, vdd_list = [], []
for inst in inst_list:
vss_list += inst.get_all_port_pins('VSS')
vdd_list += inst.get_all_port_pins('VDD')
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# export input and output
self.reexport(m_inst.get_port('in'))
self.reexport(s_inst.get_port('out'))
self.reexport(s_inst.get_port('outb'))
# connect output of m_inst to input of s_inst
self.connect_to_track_wires(s_inst.get_pin('in'), m_inst.get_pin('out'))
# connect rst
if rst:
rst_sig = self.connect_wires([m_inst.get_pin('rst'), s_inst.get_pin('rst')])
self.add_pin('rst', rst_sig)
# export scan pins
if scan:
self.reexport(m_inst.get_port('scan_in'))
self.reexport(m_inst.get_port('scan_en'))
# connect clk to clkb of m_inst and input of inverter
if not seg_ck > 0:
raise ValueError("only seg_ck > 0 currently supported")
clk_hm = m_inst.get_pin('clkb')
self.connect_to_track_wires([clk_hm, b_inst.get_pin('in')], s_inst.get_pin('clk_vm'))
self.add_pin('clk', clk_hm)
# connect clkb to clk of m_inst and output of inverter
self.connect_to_track_wires([m_inst.get_pin('clk'), s_inst.get_pin('clkb')],
b_inst.get_pin('out'))
# set properties
self.sch_params = dict(
m_params=m_master.sch_params,
s_params=s_master.sch_params,
inv_params=inv_master.sch_params if inv_master else None
)
[docs]class ScanRstLatchCore(MOSBase):
"""A tristate inverter based latch with reset pin and scanability, optimized."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._seg_in = None
@property
[docs] def seg_in(self) -> int:
return self._seg_in
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
# noinspection PyTypeChecker
return ModuleDB.get_schematic_class('bag3_digital', 'scan_rst_latch2')
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg_dict='Dictionary of number of segments.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
dual_output='True to show out and outb',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
dual_output=False,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
hm_layer = pinfo.conn_layer + 1
vm_layer = hm_layer + 1
if pinfo.top_layer < vm_layer:
raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}')
# setup floorplan
self.draw_base(pinfo)
seg_dict: Mapping[str, int] = self.params['seg_dict']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
dual_output: bool = self.params['dual_output']
# compute track locations and create masters
tr_manager = pinfo.tr_manager
# tr_w_v = tr_manager.get_width(vm_layer, 'sig')
if sig_locs is None:
sig_locs = {}
ng0 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
# ng1 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
pg0 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-3)
pg1 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2)
pg2 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)
nd0 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
nd1 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
# pd0 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
pd2 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=2)
seg_kp = seg_dict['keep']
seg_tin = seg_dict['in']
seg_pg = seg_dict['pass']
seg_nor = seg_dict['nor']
for key, val in seg_dict.items():
if val != 1:
raise ValueError('Layout optimized for seg = 1 only')
nor_sig_locs = dict(
nin0=sig_locs.get('rst', pg0),
nin1=sig_locs.get('nclk', pg1),
pout=sig_locs.get('pout', pd2),
nout=sig_locs.get('nout', nd1),
)
params = dict(pinfo=pinfo, seg=seg_nor, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
sig_locs=nor_sig_locs)
nor_master = self.new_template(NOR2Core, params=params)
# key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
tin_sig_locs = dict(
# nin=sig_locs.get(key, pg2),
# pout=pd0,
# nout=nd0,
# nen=sig_locs.get('nclk_in', ng2),
# pen=sig_locs.get('pclkb_in', pg0),
)
params = dict(pinfo=pinfo, seg=seg_tin, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=tin_sig_locs)
tin_master = self.new_template(InvTristateCore, params=params)
key = 'in' if 'in' in sig_locs else ('nin' if 'nin' in sig_locs else 'pin')
tscan_sig_locs = dict(
nin=sig_locs.get(key, pg1),
# pout=pd0,
# nout=nd1,
# nen=sig_locs.get('nclk_scan', ng2),
# pen=sig_locs.get('pclkb_scan', pg0),
)
params = dict(pinfo=pinfo, seg=seg_tin, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=tscan_sig_locs)
tscan_master = self.new_template(InvTristateCore, params=params)
kp_sig_locs = dict(
# nin=sig_locs.get('pout', pg1),
pout=pd1,
nout=nd0,
# nen=sig_locs.get('nclkb', ng2),
# pen=sig_locs.get('pclk', pg0),
)
params = dict(pinfo=pinfo, seg=seg_kp, w_p=w_p, w_n=w_n, ridx_p=ridx_p, ridx_n=ridx_n,
vertical_out=False, sig_locs=kp_sig_locs)
kp_master = self.new_template(InvTristateCore, params=params)
# set size
blk_sp = max(self.min_sep_col, self.get_hm_sp_le_sep_col())
tin_ncol = tin_master.num_cols
kp_ncol = kp_master.num_cols
nor_ncol = nor_master.num_cols
num_cols = tin_ncol + blk_sp + 2 * seg_pg + blk_sp + tin_ncol + blk_sp + kp_ncol + blk_sp + nor_ncol
self.set_mos_size(num_cols, 1)
# --- Placement --- #
# add instances
cur_col = 0
tin = self.add_tile(tin_master, 0, cur_col)
cur_col += tin_ncol + blk_sp
passg_n = self.add_mos(ridx_n, cur_col, 2 * seg_pg)
passg_p = self.add_mos(ridx_p, cur_col, 2 * seg_pg)
cur_col += 2 * seg_pg + blk_sp
tscan = self.add_tile(tscan_master, 0, cur_col + tin_ncol, flip_lr=True)
cur_col += tin_ncol + blk_sp
kp = self.add_tile(kp_master, 0, cur_col + kp_ncol, flip_lr=True)
cur_col += kp_ncol + blk_sp
nor = self.add_tile(nor_master, 0, cur_col)
# --- Routing --- #
# connect/export VSS/VDD
vss_list, vdd_list = [], []
inst_list = [tin, tscan, kp, nor]
for inst in inst_list:
vss_list.append(inst.get_pin('VSS'))
vdd_list.append(inst.get_pin('VDD'))
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
# export input and scan_input
self.reexport(tin.get_port('nin'), net_name='in', hide=False)
self.reexport(tscan.get_port('nin'), net_name='scan_in', hide=False)
# export rst
self.reexport(nor.get_port('nin<0>'), net_name='rst', hide=False)
# connect clk and clkb
clk_hm_tid = TrackID(hm_layer, ng0)
clkb_hm_tid = TrackID(hm_layer, pg2)
clk_hm_l = self.connect_to_tracks(passg_n.g, clk_hm_tid, min_len_mode=MinLenMode.MIDDLE)
clkb_hm_l = self.connect_to_tracks(passg_p.g, clkb_hm_tid, min_len_mode=MinLenMode.MIDDLE)
self.add_pin('clk_pass', clk_hm_l, label='clk:')
self.add_pin('clk_keep', kp.get_pin('enb'), label='clk:')
self.add_pin('clkb_pass', clkb_hm_l, label='clkb:')
self.add_pin('clkb_keep', kp.get_pin('en'), label='clkb:')
# connect scan and scanb
self.add_pin('scan_en', tscan.get_pin('en'), label='scan_en:')
self.add_pin('in_en', tin.get_pin('enb'), label='scan_en:')
self.add_pin('scan_enb', tscan.get_pin('enb'), label='scan_enb:')
self.add_pin('in_enb', tin.get_pin('en'), label='scan_enb:')
# connect in and scan tristate outputs to passgate
self.connect_to_track_wires(passg_p.s[0], tin.get_pin('pout'))
self.connect_to_track_wires(passg_p.s[1], tscan.get_pin('pout'))
self.connect_to_track_wires(passg_n.s[0], tin.get_pin('nout'))
self.connect_to_track_wires(passg_n.s[1], tscan.get_pin('nout'))
self.connect_wires([passg_p.s[0], passg_n.s[0]])
self.connect_wires([passg_p.s[1], passg_n.s[1]])
# connect outb
poutb = self.connect_to_track_wires(passg_p.d, kp.get_pin('pout'))
noutb = self.connect_to_track_wires(passg_n.d, kp.get_pin('nout'))
out_vm = nor.get_pin('out')
outb_vm_tid = tr_manager.get_next_track_obj(out_vm, 'sig', 'sig')
outb = self.connect_to_tracks([poutb, noutb, nor.get_pin('nin<1>')], outb_vm_tid)
self.add_pin('out', out_vm)
self.add_pin('outb', outb, hide=not dual_output)
self.reexport(nor.get_port('pout'))
self.reexport(nor.get_port('nout'))
# connect out to input of keeper tristate
self.connect_to_track_wires(kp.get_pin('nin'), out_vm)
passg_sch_params = nor_master.sch_params.copy()
passg_sch_params['seg'] = seg_pg
# set properties
self.sch_params = dict(
tin=tin_master.sch_params,
tfb=kp_master.sch_params,
nor=nor_master.sch_params,
passg=passg_sch_params,
dual_output=dual_output,
)
[docs]class ScanRstFlopCore(MOSBase):
"""A tristate inverter based flip-flop in 2 rows with 1 row latches."""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
self._cntr_col_clk = None
@property
[docs] def seg_in(self):
return self.sch_params['seg_m']['in']
@property
[docs] def cntr_col_clk(self):
return self._cntr_col_clk
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
# noinspection PyTypeChecker
return ModuleDB.get_schematic_class('bag3_digital', 'scan_rst_flop')
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg_dict='Dictionary of number of segments.',
w_p='pmos width.',
w_n='nmos width.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Signal track location dictionary.',
dual_output='True to export out and outb',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
dual_output=False,
)
[docs] def draw_layout(self):
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
seg_dict: Mapping[str, int] = self.params['seg_dict']
w_p: int = self.params['w_p']
w_n: int = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
# sig_locs: Optional[Mapping[str, float]] = self.params['sig_locs']
dual_output: bool = self.params['dual_output']
# setup floorplan
self.draw_base(pinfo)
# compute track locations
# if sig_locs is None:
# sig_locs = {}
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
# ng0 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
ng1 = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
# pg0 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-3)
pg1 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2)
pg2 = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)
# nd0 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
nd1 = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
# pd0 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)
pd1 = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=1)
# make masters
s_sig_locs = {
'rst': pg1,
'pout': pg2,
'in': pg2,
}
lat_params = dict(pinfo=pinfo, seg_dict=seg_dict, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, vertical_clk=False)
s_params = dict(**lat_params, sig_locs=s_sig_locs)
s_master = self.new_template(RstLatchCore, params=s_params)
m_params = dict(**lat_params, dual_output=False)
m_master = self.new_template(ScanRstLatchCore, params=m_params)
clk_inv_sig_locs = {
'pout': pd1,
'nout': nd1,
}
params = dict(pinfo=pinfo, seg=seg_dict['inv'], w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs=clk_inv_sig_locs, vertical_out=False)
clk_inv_master = self.new_template(InvCore, params=params)
scan_inv_sig_locs = {
'nin': ng1,
}
params = dict(pinfo=pinfo, seg=seg_dict['inv'], w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs=scan_inv_sig_locs, vertical_out=False)
scan_inv_master = self.new_template(InvCore, params=params)
blk_sp = max(self.min_sep_col, self.get_hm_sp_le_sep_col())
m_ncol = m_master.num_cols
s_ncol = s_master.num_cols
inv_ncol = clk_inv_master.num_cols
num_cols = max(m_ncol, inv_ncol + blk_sp + inv_ncol + blk_sp + s_ncol)
# set size
self.set_mos_size(num_cols)
# add instances
m_inst = self.add_tile(m_master, 0, num_cols - m_ncol)
cur_col = num_cols - s_ncol
s_inst = self.add_tile(s_master, 1, cur_col)
cur_col -= (blk_sp + inv_ncol)
clk_inv_inst = self.add_tile(clk_inv_master, 1, cur_col + inv_ncol, flip_lr=True)
# flipped to maintain VSS connection polarity
cur_col -= (blk_sp + inv_ncol)
scan_inv_inst = self.add_tile(scan_inv_master, 1, cur_col)
inst_list = [m_inst, s_inst, clk_inv_inst, scan_inv_inst]
# connect/export VSS/VDD
vss_list, vdd_list = [], []
for inst in inst_list:
vss_list += inst.get_all_port_pins('VSS')
vdd_list += inst.get_all_port_pins('VDD')
self.add_pin('VSS', self.connect_wires(vss_list))
self.add_pin('VDD', self.connect_wires(vdd_list))
out_int = m_inst.get_pin('out')
self.reexport(s_inst.get_port('out'))
self.reexport(s_inst.get_port('mid_vm'), net_name='outb', hide=not dual_output)
# connect rst of m_inst and s_inst
rst = m_inst.get_pin('rst')
self.add_pin('rst', rst)
rst_vm_tid = self.tr_manager.get_next_track_obj(out_int, 'sig', 'sig', count_rel_tracks=-1)
self.connect_to_tracks([rst, s_inst.get_pin('nrst')], rst_vm_tid)
# connect output of m_inst to input of s_inst
s_in = s_inst.get_pin('nin')
int_vm_idx = self.grid.coord_to_track(vm_layer, s_in.middle, mode=RoundMode.NEAREST)
self.connect_to_tracks([s_in, m_inst.get_pin('pout')], TrackID(vm_layer, int_vm_idx))
# connect clk and clkb
clk_keep_m = m_inst.get_pin('clk_keep')
clk_r_vm_idx = self.grid.coord_to_track(vm_layer, clk_keep_m.middle, mode=RoundMode.NEAREST)
clkb_r_vm_idx = self.tr_manager.get_next_track(vm_layer, clk_r_vm_idx, 'sig', 'sig',
up=True)
self.connect_to_tracks([clk_inv_inst.get_pin('nin'), s_inst.get_pin('nclk'),
s_inst.get_pin('pclk'), m_inst.get_pin('clkb_keep')],
TrackID(vm_layer, clk_r_vm_idx))
self.connect_to_tracks([s_inst.get_pin('nclkb'), s_inst.get_pin('pclkb'),
clk_keep_m, clk_inv_inst.get_pin('pout'),
clk_inv_inst.get_pin('nout')],
TrackID(vm_layer, clkb_r_vm_idx))
clk_pass_m = m_inst.get_pin('clk_pass')
clk_l_vm_idx = self.grid.coord_to_track(vm_layer, clk_pass_m.middle, mode=RoundMode.NEAREST)
clkb_l_vm_idx = self.tr_manager.get_next_track(vm_layer, clk_l_vm_idx, 'sig', 'sig',
up=True)
self.connect_to_tracks([clk_inv_inst.get_pin('nin'), m_inst.get_pin('clkb_pass')],
TrackID(vm_layer, clk_l_vm_idx))
self.connect_to_tracks([clk_inv_inst.get_pin('pout'), clk_inv_inst.get_pin('nout'),
clk_pass_m], TrackID(vm_layer, clkb_l_vm_idx))
# connect scan_en and scan_enb
scan_en_r_vm_idx = self.tr_manager.get_next_track(vm_layer, int_vm_idx, 'sig', 'sig',
up=True)
scan_enb_r_vm_idx = self.tr_manager.get_next_track(vm_layer, int_vm_idx, 'sig', 'sig',
up=False)
self.connect_to_tracks([scan_inv_inst.get_pin('nin'), m_inst.get_pin('scan_en')],
TrackID(vm_layer, scan_en_r_vm_idx))
self.connect_to_tracks([m_inst.get_pin('scan_enb'), scan_inv_inst.get_pin('pout'),
scan_inv_inst.get_pin('nout')],
TrackID(vm_layer, scan_enb_r_vm_idx))
in_en_m = m_inst.get_pin('in_en')
scan_en_l_vm_idx = self.grid.coord_to_track(vm_layer, in_en_m.middle,
mode=RoundMode.NEAREST)
scan_enb_l_vm_idx = self.tr_manager.get_next_track(vm_layer, scan_en_l_vm_idx, 'sig', 'sig',
up=False)
self.connect_to_tracks([scan_inv_inst.get_pin('nin'), in_en_m],
TrackID(vm_layer, scan_en_l_vm_idx))
self.connect_to_tracks([m_inst.get_pin('in_enb'), scan_inv_inst.get_pin('pout'),
scan_inv_inst.get_pin('nout')],
TrackID(vm_layer, scan_enb_l_vm_idx))
# export pins
self.reexport(m_inst.get_port('in'))
self.reexport(m_inst.get_port('scan_in'))
self.reexport(scan_inv_inst.get_port('nin'), net_name='scan_en', hide=False)
self.reexport(clk_inv_inst.get_port('nin'), net_name='clk', hide=False)
# set properties
self.sch_params = dict(
m_params=m_master.sch_params,
s_params=s_master.sch_params,
inv_params=clk_inv_master.sch_params,
dual_output=dual_output,
)