First version of CharacterizationWrapper.
This class contains periphery to enabling measuring setup/hold times and
clk2q delays.
diff --git a/doitcode/sram.py b/doitcode/sram.py
index ef78f77..cfde4a4 100644
--- a/doitcode/sram.py
+++ b/doitcode/sram.py
@@ -1,9 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-or-later
-from typing import Optional, Tuple, Union, cast
+from typing import Optional, List, Tuple, Union, cast
+from pandas import notna
from pdkmaster.technology import geometry as _geo, primitive as _prm
-from pdkmaster.design import library as _lbry
+from pdkmaster.design import circuit as _ckt, library as _lbry
+from c4m import flexmem as _mem
from c4m.pdk import sky130
from . import frame as _frm
@@ -97,6 +99,1416 @@
}
+class SPCharacterizationWrapper(_lbry._OnDemandCell[_lbry.Library]):
+ def __init__(self, *,
+ lib: _lbry.Library, spcell: _mem.sp6t.factory._SP6TBlock,
+ ):
+ name = f"Char{spcell.name}"
+ super().__init__(lib=lib, name=name)
+ self.spcell = spcell
+
+ def _create_circuit(self):
+ stdcells = sky130.stdcelllib.cells
+ ff_cell = stdcells.sff1_x4
+ mux_cell = stdcells.mx2_x2
+
+ spcell = self.spcell
+ self.a_bits = a_bits = spcell.a_bits
+ self.word_size = word_size = spcell.word_size
+ self.we_size = we_size = spcell.we_size
+ assert word_size%we_size == 0
+ self.byte_size = byte_size = word_size//we_size
+
+ ckt = self.new_circuit()
+
+ # Registers for shifting and capturing the inputs for the sram
+ # Order is aligned with the layout
+ _in_shiftreg: List[_ckt._CellInstance] = []
+ _in_capturereg: List[_ckt._CellInstance] = []
+ for n_a in range(a_bits):
+ _in_shiftreg.append(ckt.instantiate(ff_cell, name=f"aff_s[{n_a}]"))
+ _in_capturereg.append(ckt.instantiate(ff_cell, name=f"aff_c[{n_a}]"))
+ for n_we in range(we_size):
+ _in_shiftreg.append(ckt.instantiate(ff_cell, name=f"weff_s[{n_we}]"))
+ _in_capturereg.append(ckt.instantiate(ff_cell, name=f"weff_c[{n_we}]"))
+ for n_byte in range(byte_size):
+ n_bit = n_we*byte_size + n_byte
+
+ _in_shiftreg.append(ckt.instantiate(ff_cell, name=f"dff_s[{n_bit}]"))
+ _in_capturereg.append(ckt.instantiate(ff_cell, name=f"dff_c[{n_bit}]"))
+ in_shiftreg = tuple(_in_shiftreg)
+ in_capturereg = tuple(_in_capturereg)
+ in_all = (*in_shiftreg, *in_capturereg)
+
+ # Register for capturing the outputs
+ _out_shiftreg: List[_ckt._CellInstance] = []
+ _out_capturereg: List[_ckt._CellInstance] = []
+ _out_muxes: List[_ckt._CellInstance] = []
+ for n_bit in range(word_size):
+ _out_shiftreg.append(ckt.instantiate(ff_cell, name=f"qff_s[{n_bit}]"))
+ _out_capturereg.append(ckt.instantiate(ff_cell, name=f"qff_c[{n_bit}]"))
+ _out_muxes.append(ckt.instantiate(mux_cell, name=f"qmux[{n_bit}]"))
+ out_shiftreg = tuple(_out_shiftreg)
+ out_capturereg = tuple(_out_capturereg)
+ out_muxes = tuple(_out_muxes)
+ out_all = (*out_shiftreg, *out_capturereg, *out_muxes)
+
+ in_captclkff = ckt.instantiate(ff_cell, name="incaptclkff")
+ out_captclkff = ckt.instantiate(ff_cell, name="outcaptclkff")
+
+ sram = ckt.instantiate(spcell, name="sram")
+
+ ckt.new_net(name="dvss", external=True, childports=(
+ inst.ports.vss for inst in (*in_all, *out_all, in_captclkff, out_captclkff)
+ ))
+ ckt.new_net(name="dvdd", external=True, childports=(
+ inst.ports.vdd for inst in (*in_all, *out_all, in_captclkff, out_captclkff)
+ ))
+ ckt.new_net(name="svss", external=True, childports=sram.ports.vss)
+ ckt.new_net(name="svdd", external=True, childports=sram.ports.vdd)
+
+ ckt.new_net(name="shift_in", external=True, childports=in_all[0].ports.i)
+ ckt.new_net(name="shift_out", external=True, childports=out_shiftreg[-1].ports.q)
+
+ ckt.new_net(name="sramclk", external=True, childports=(
+ sram.ports.clk, in_captclkff.ports.ck, out_captclkff.ports.ck,
+ ))
+
+ ckt.new_net(name="in_shiftclk", external=True, childports=(
+ ff.ports.ck for ff in in_shiftreg
+ ))
+ ckt.new_net(name="in_captureclk", external=True, childports=(
+ *(ff.ports.ck for ff in in_capturereg), in_captclkff.ports.i,
+ ))
+ ckt.new_net(name="in_captureclk_l", external=True, childports=(
+ in_captclkff.ports.q
+ ))
+
+ ckt.new_net(name="out_shiftclk", external=True, childports=(
+ ff.ports.ck for ff in out_shiftreg
+ ))
+ ckt.new_net(name="out_captureclk", external=True, childports=(
+ *(ff.ports.ck for ff in out_capturereg), out_captclkff.ports.i,
+ ))
+ ckt.new_net(name="out_captureclk_l", external=True, childports=(
+ out_captclkff.ports.q
+ ))
+ ckt.new_net(name="out_docapture", external=True, childports=(
+ mux.ports.cmd for mux in out_muxes
+ ))
+
+ # Connect the input shift registers
+ # Output of flop in shift reg is connected to input of corresponding flop in
+ # the capture reg and to the input of next flop in shift reg. Last flop
+ # connected to the input
+ # Output of flop in capture reg is connected to corresponding sram input pin
+ n = 0
+ for n_a in range(a_bits):
+ ckt.new_net(name=f"a_s[{n_a}]", external=False, childports=(
+ in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
+ in_shiftreg[n + 1].ports.i
+ ))
+ ckt.new_net(name=f"a_c[{n_a}]", external=False, childports=(
+ in_capturereg[n].ports.q, sram.ports[f"a[{n_a}]"],
+ ))
+ n += 1
+ for n_we in range(we_size):
+ ckt.new_net(name=f"we_s[{n_we}]", external=False, childports=(
+ in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
+ in_shiftreg[n + 1].ports.i
+ ))
+ ckt.new_net(name=f"we_c[{n_we}]", external=False, childports=(
+ in_capturereg[n].ports.q, sram.ports[f"we[{n_we}]"],
+ ))
+ n += 1
+
+ for n_byte in range(byte_size):
+ n_bit = n_we*byte_size + n_byte
+ nextgatepin = (
+ in_shiftreg[n + 1].ports.i
+ if n < (len(in_shiftreg) - 1)
+ else out_muxes[0].ports.i1
+ )
+ ckt.new_net(name=f"d_s[{n_bit}]", external=False, childports=(
+ in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
+ nextgatepin,
+ ))
+ ckt.new_net(name=f"d_c[{n_bit}]", external=False, childports=(
+ in_capturereg[n].ports.q, sram.ports[f"d[{n_bit}]"],
+ ))
+ n += 1
+
+ # Connect the output shift registers
+ # The output of the sram is connected to the input of the corresponding flop
+ # in the capture reg. The output of the flop in the capture reg is connect
+ # to input i0 of the corresponding mux and the output of the previous flop in
+ # shift reg is connect to i1 of the mux.
+ for n_bit in range(word_size):
+ ckt.new_net(name=f"q_c[{n_bit}]", external=False, childports=(
+ sram.ports[f"q[{n_bit}]"], out_capturereg[n_bit].ports.i,
+ ))
+ ckt.new_net(name=f"q_cl[{n_bit}]", external=False, childports=(
+ out_capturereg[n_bit].ports.q, out_muxes[n_bit].ports.i0,
+ ))
+ if n_bit < (word_size - 1):
+ ckt.new_net(name=f"q_s[{n_bit}]", external=False, childports=(
+ out_shiftreg[n_bit].ports.q, out_muxes[n_bit + 1].ports.i1,
+ ))
+ ckt.new_net(name=f"q_mux[{n_bit}]", external=False, childports=(
+ out_muxes[n_bit].ports.q, out_shiftreg[n_bit].ports.i,
+ ))
+
+ def _create_layout(self):
+ tech = self.tech
+ prims = tech.primitives
+
+ ckt = self.circuit
+ nets = ckt.nets
+ insts = ckt.instances
+
+ nwm = cast(_prm.Well, prims.nwm)
+ li = cast(_prm.MetalWire, prims.li)
+ assert li.pin is not None
+ lipin = li.pin[0]
+ mcon = cast(_prm.Via, prims.mcon)
+ m1 = cast(_prm.MetalWire, prims.m1)
+ assert m1.pin is not None
+ m1pin = m1.pin[0]
+ via = cast(_prm.Via, prims.via)
+ m2 = cast(_prm.MetalWire, prims.m2)
+ assert m2.pin is not None
+ m2pin = m2.pin[0]
+
+ # Place the SRAM block
+ layouter = self.new_circuitlayouter()
+
+ sram_lay = layouter.place(insts.sram, origin=_geo.origin)
+ sram_bb = sram_lay.boundary
+ assert sram_bb is not None
+
+ sramclk_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=nets.sramclk, depth=1)
+
+ # Place and connect a flipflops
+ x_s = y_s0 = None
+ x_c = y_c0 = None
+ dy = None
+ net_s_prev = None
+ mconnets_m1bb1_prev = None
+ mconacclk_m1bb_last = None
+ mconasclk_m1bb_last = None
+ affcvss_lipinbb_last = None
+ affcvdd_lipinbb_last = None
+ affsvss_lipinbb_last = None
+ affsvdd_lipinbb_last = None
+ for n_a in range(self.a_bits):
+ net_s = nets[f"a_s[{n_a}]"]
+ net_c = nets[f"a_c[{n_a}]"]
+ _affs_lay = layouter.inst_layout(
+ inst=insts[f"aff_s[{n_a}]"], rotation=_geo.Rotation.MX90,
+ )
+ _affc_lay = layouter.inst_layout(
+ inst=insts[f"aff_c[{n_a}]"], rotation=_geo.Rotation.R90,
+ )
+ sramnetc_m1pinbb = sram_lay.bounds(mask=m1pin.mask, net=net_c, depth=1)
+ if n_a == 0:
+ _affs_bb = _affs_lay.boundary
+ assert _affs_bb is not None
+ _affc_bb = _affc_lay.boundary
+ assert _affc_bb is not None
+
+ # Compute placement based on first flops
+ x_c = -3.0 - _affc_bb.right
+ y_c0 = sramnetc_m1pinbb.bottom - _affc_bb.top
+ x_s = x_c + _affc_bb.left - _affs_bb.right
+ y_s0 = sramnetc_m1pinbb.bottom - _affs_bb.top
+
+ dy = _affs_bb.height
+ assert x_s is not None
+ assert y_s0 is not None
+ assert x_c is not None
+ assert y_c0 is not None
+ assert dy is not None
+
+ # Place flops for a part of registers
+ y_s = y_s0 + n_a*dy
+ y_c = y_c0 + n_a*dy
+ affs_lay = layouter.place(_affs_lay, x=x_s, y=y_s)
+ affsnets_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net_s)
+ affssclk_lipinbb = affs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
+ affsvss_lipinbb_last = affs_lay.bounds(mask=lipin.mask, net=nets.dvss)
+ affsvdd_lipinbb_last = affs_lay.bounds(mask=lipin.mask, net=nets.dvdd)
+ affc_lay = layouter.place(_affc_lay, x=x_c, y=y_c)
+ affcnets_lipinbb = affc_lay.bounds(mask=lipin.mask, net=net_s)
+ affcnetc_lipinbb = affc_lay.bounds(mask=lipin.mask, net=net_c)
+ affccclk_lipinbb = affc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
+ affcvss_lipinbb_last = affc_lay.bounds(mask=lipin.mask, net=nets.dvss)
+ affcvdd_lipinbb_last = affc_lay.bounds(mask=lipin.mask, net=nets.dvdd)
+
+ # in_shiftclk
+ mconsclk_lay = layouter.add_wire(
+ net=nets.in_shiftclk, wire=mcon, columns=2,
+ origin=affssclk_lipinbb.center,
+ )
+ mconasclk_m1bb_last = mconsclk_lay.bounds(mask=m1.mask)
+
+ # in_captureclk
+ mconcclk_lay = layouter.add_wire(
+ net=nets.in_captureclk, wire=mcon, columns=2,
+ origin=affccclk_lipinbb.center,
+ )
+ mconacclk_m1bb_last = mconcclk_lay.bounds(mask=m1.mask)
+
+ # net_s
+ _mconnets_lay = layouter.wire_layout(
+ net=net_s, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconnets_libb = _mconnets_lay.bounds(mask=li.mask)
+
+ x = affsnets_lipinbb.right - _mconnets_libb.right
+ y = affsnets_lipinbb.center.y
+ mconnets_lay1 = layouter.place(_mconnets_lay, x=x, y=y)
+ mconnets_m1bb1 = mconnets_lay1.bounds(mask=m1.mask)
+ x = affcnets_lipinbb.left - _mconnets_libb.left
+ y = affcnets_lipinbb.center.y
+ mconnets_lay2 = layouter.place(_mconnets_lay, x=x, y=y)
+ mconnets_m1bb2 = mconnets_lay2.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(rect=mconnets_m1bb2, top=mconnets_m1bb1.top)
+ layouter.add_wire(net=net_s, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconnets_m1bb1, right=mconnets_m1bb2.right)
+ layouter.add_wire(net=net_s, wire=m1, shape=shape)
+
+ _mconnetc_lay = layouter.wire_layout(
+ net=net_c, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconnetc_libb = _mconnetc_lay.bounds(mask=li.mask)
+
+ x = affcnetc_lipinbb.right - _mconnetc_libb.right
+ y = affcnetc_lipinbb.center.y
+ mconnetc_lay = layouter.place(_mconnetc_lay, x=x, y=y)
+ mconnetc_m1bb = mconnetc_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(rect=sramnetc_m1pinbb, left = mconnetc_m1bb.left)
+ layouter.add_wire(net=net_c, wire=m1, shape=shape)
+ if sramnetc_m1pinbb.top > mconnetc_m1bb.top:
+ shape = _geo.Rect.from_rect(rect=mconnetc_m1bb, top=sramnetc_m1pinbb.top)
+ else:
+ assert sramnetc_m1pinbb.bottom < mconnetc_m1bb.bottom
+ shape = _geo.Rect.from_rect(
+ rect=mconnetc_m1bb, bottom=sramnetc_m1pinbb.bottom,
+ )
+ layouter.add_wire(net=net_c, wire=m1, shape=shape)
+
+ # net_s_prev
+ if net_s_prev is not None:
+ affsnetsp_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net_s_prev)
+
+ assert mconnets_m1bb1_prev is not None
+ _mconnetsp_lay = layouter.wire_layout(
+ net=net_s_prev, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconnetsp_libb = _mconnets_lay.bounds(mask=li.mask)
+
+ x = affsnetsp_lipinbb.right - _mconnetsp_libb.right
+ y = affsnetsp_lipinbb.center.y
+ mconnetsp_lay = layouter.place(_mconnetsp_lay, x=x, y=y)
+ mconnetsp_m1bb = mconnetsp_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=mconnets_m1bb1_prev, top=mconnetsp_m1bb.top,
+ )
+ layouter.add_wire(net=net_s_prev, wire=m1, shape=shape)
+ else:
+ assert n_a == 0
+ net = nets.shift_in
+ affssin_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net)
+ _mconsin_lay = layouter.wire_layout(
+ net=net, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconsin_libb = _mconsin_lay.bounds(mask=li.mask)
+
+ x = affssin_lipinbb.left - _mconsin_libb.left
+ y = affssin_lipinbb.center.y
+ mconsin_lay = layouter.place(_mconsin_lay, x=x, y=y)
+ mconsin_m1bb = mconsin_lay.bounds(mask=m1.mask)
+
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=mconsin_m1bb)
+
+ net_s_prev = net_s
+ mconnets_m1bb1_prev = mconnets_m1bb1
+ assert x_s is not None
+ assert y_s0 is not None
+ assert x_c is not None
+ assert y_c0 is not None
+ assert dy is not None
+ assert mconacclk_m1bb_last is not None
+ assert mconasclk_m1bb_last is not None
+ assert affcvss_lipinbb_last is not None
+ assert affcvdd_lipinbb_last is not None
+ assert affsvss_lipinbb_last is not None
+ assert affsvdd_lipinbb_last is not None
+
+ # Place in_captureclk capture flop + connect
+ x = x_c
+ y = y_c0 - dy
+ cclkff_lay = layouter.place(
+ insts.incaptclkff, x=x, y=y, rotation=_geo.Rotation.R90,
+ )
+ cclkffclk_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.sramclk)
+ cclkffcclk_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
+ cclkffcclkl_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.in_captureclk_l)
+
+ x = mconacclk_m1bb_last.center.x
+ y = cclkffcclk_lipinbb.center.y
+ layouter.add_wire(
+ net=nets.in_captureclk, wire=mcon, x=x, y=y, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+
+ # Connect sramclk
+ net = nets.sramclk
+
+ _mconclkcclkff_lay = layouter.wire_layout(
+ net=net, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconclkcclkff_libb = _mconclkcclkff_lay.bounds(mask=li.mask)
+
+ x = cclkffclk_lipinbb.right - _mconclkcclkff_libb.right
+ y = cclkffclk_lipinbb.center.y
+ layouter.place(_mconclkcclkff_lay, x=x, y=y)
+ viaclkcclkff_lay = layouter.add_wire(
+ net=net, wire=via, columns=2, x=x, y=y,
+ bottom_enclosure="tall", top_enclosure="tall",
+ )
+ viaclkcclkff_m2bb = viaclkcclkff_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=viaclkcclkff_m2bb, right=sramclk_m2pinbb.right,
+ )
+ layouter.add_wire(net=net, wire=m2, shape=shape)
+ shape = _geo.Rect.from_rect(
+ rect=sramclk_m2pinbb, top=viaclkcclkff_m2bb.top,
+ )
+ layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
+
+ # Connect in_captureclk_l
+ _mconcclklcclkff_lay = layouter.wire_layout(
+ net=nets.in_captureclk_l, wire=mcon, columns=2,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ _mconcclklcclkff_libb = _mconcclklcclkff_lay.bounds(mask=li.mask)
+
+ x = cclkffcclkl_lipinbb.left - _mconcclklcclkff_libb.left
+ y = cclkffcclkl_lipinbb.center.y
+ mconcclklcclkff_lay = layouter.place(_mconcclklcclkff_lay, x=x, y=y)
+ mconcclklcclkff_m1bb = mconcclklcclkff_lay.bounds(mask=m1.mask)
+
+ layouter.add_wire(
+ net=nets.in_captureclk_l, wire=m1, pin=m1pin, shape=mconcclklcclkff_m1bb,
+ )
+
+ # Compute horizontal displacement of the standard cells
+ assert self.byte_size > 2
+ m2pinbb1 = sram_lay.bounds(mask=m2pin.mask, net=nets["d_c[0]"], depth=1)
+ m2pinbb2 = sram_lay.bounds(mask=m2pin.mask, net=nets["d_c[2]"], depth=1)
+ dx_stdcell = m2pinbb2.center.x - m2pinbb1.center.x
+
+ # Place and connect we, d, q flipflops
+ insreg_m1bb_prev = None
+ insreg_net_prev = None
+ outsreg_m1bb_prev = None
+ outsreg_net_prev = None
+ outsreg_m1bb_first = None
+ outsreg_net_first = None
+ mconwecclk_m1bb_last = None
+ mconwesclk_m1bb_last = None
+ mcondcclk_m1bb1_last = None
+ mcondcclk_m1bb2_last = None
+ mcondsclk_m1bb1_last = None
+ mcondsclk_m1bb2_last = None
+ mconqcclk_m1bb1_last = None
+ mconqcclk_m1bb2_last = None
+ mconqsclk_m1bb1_last = None
+ mconqsclk_m1bb2_last = None
+ mcondoc_m1bb_first = None
+ mcondoc_m1bb_last = None
+ for n_we in range(self.we_size):
+ # Place we flops
+ we_s = nets[f"we_s[{n_we}]"]
+ we_c = nets[f"we_c[{n_we}]"]
+ sramwe_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=we_c, depth=1)
+
+ _weffc_lay = layouter.inst_layout(inst=insts[f"weff_c[{n_we}]"])
+ _weffc_bb = _weffc_lay.boundary
+ assert _weffc_bb is not None
+ _weffs_lay = layouter.inst_layout(
+ inst=insts[f"weff_s[{n_we}]"], rotation=_geo.Rotation.MX,
+ )
+ _weffs_bb = _weffs_lay.boundary
+ assert _weffs_bb is not None
+
+ stdcell_left = sramwe_m2pinbb.left - 5.0
+ x = stdcell_left - _weffc_bb.left
+ y = -5.0 - _weffc_bb.top
+ weffc_lay = layouter.place(_weffc_lay, x=x, y=y)
+ weffc_bb = weffc_lay.boundary
+ assert weffc_bb is not None
+ weffcclk_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
+ weffcwes_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=we_s)
+ weffcwec_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=we_c)
+
+ x = stdcell_left - _weffs_bb.left
+ y = weffc_bb.bottom - _weffs_bb.top
+ weffs_lay = layouter.place(_weffs_lay, x=x, y=y)
+ weffs_bb = weffs_lay.boundary
+ assert weffs_bb is not None
+ weffsclk_lipinbb = weffs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
+ weffswes_lipinbb = weffs_lay.bounds(mask=lipin.mask, net=we_s)
+
+ # Connect we captureclk
+ net = nets.in_captureclk
+
+ o = weffcclk_lipinbb.center
+ mconwecclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mconwecclk_m1bb = mconwecclk_lay.bounds(mask=m1.mask)
+ mconwecclk_m1bb_last = mconwecclk_m1bb
+
+ # Connect we shifteclk
+ net = nets.in_shiftclk
+
+ o = weffsclk_lipinbb.center
+ mconwesclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mconwesclk_m1bb = mconwesclk_lay.bounds(mask=m1.mask)
+ mconwesclk_m1bb_last = mconwesclk_m1bb
+
+ # Connect we_c
+ _mconwec_lay = layouter.wire_layout(
+ net=we_c, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconwec_libb = _mconwec_lay.bounds(mask=li.mask)
+
+ x = weffcwec_lipinbb.center.x
+ y = weffcwec_lipinbb.top - _mconwec_libb.top
+ mconwec_lay = layouter.place(_mconwec_lay, x=x, y=y)
+ mconwec_m1bb = mconwec_lay.bounds(mask=m1.mask)
+ x = sramwe_m2pinbb.center.x
+ viawec_lay = layouter.add_wire(net=we_c, wire=via, rows=2, x=x, y=y)
+ viawec_m1bb = viawec_lay.bounds(mask=m1.mask)
+ viawec_m2bb = viawec_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(rect=sramwe_m2pinbb, bottom=viawec_m2bb.bottom)
+ layouter.add_wire(net=we_c, wire=m2, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconwec_m1bb, left=viawec_m1bb.left)
+ layouter.add_wire(net=we_c, wire=m1, shape=shape)
+
+ # Connect we_s
+ _mconwes_lay = layouter.wire_layout(
+ net=we_s, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconwes_libb = _mconwes_lay.bounds(mask=li.mask)
+
+ x = weffswes_lipinbb.center.x
+ y = weffswes_lipinbb.top - _mconwes_libb.top
+ mconwes_lay1 = layouter.place(_mconwes_lay, x=x, y=y)
+ mconwes_m1bb1 = mconwes_lay1.bounds(mask=m1.mask)
+
+ x = weffcwes_lipinbb.center.x
+ y = weffcwes_lipinbb.bottom - _mconwes_libb.bottom
+ mconwes_lay2 = layouter.place(_mconwes_lay, x=x, y=y)
+ mconwes_m1bb2 = mconwes_lay2.bounds(mask=m1.mask)
+ insreg_m1bb_prev = mconwes_m1bb2
+ insreg_net_prev = we_s
+
+ shape = _geo.Rect.from_rect(rect=mconwes_m1bb2, bottom=mconwes_m1bb1.bottom)
+ layouter.add_wire(net=we_s, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconwes_m1bb1, left=mconwes_m1bb2.left)
+ layouter.add_wire(net=we_s, wire=m1, shape=shape)
+
+ # Place d flops, q flops and muxes
+ y_d = None
+ for n_byte in range(self.byte_size):
+ n_bit = n_we*self.byte_size + n_byte
+ d_s = nets[f"d_s[{n_bit}]"]
+ d_c = nets[f"d_c[{n_bit}]"]
+ sramd_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=d_c, depth=1)
+ q_c = nets[f"q_c[{n_bit}]"]
+ q_cl = nets[f"q_cl[{n_bit}]"]
+ q_mux = nets[f"q_mux[{n_bit}]"]
+ if n_bit < (self.word_size - 1):
+ q_s = nets[f"q_s[{n_bit}]"]
+ else:
+ q_s = nets.shift_out
+ sramq_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=q_c, depth=1)
+
+ mux_below = (n_byte%2) == 0
+
+ # Place the d flops, put two underneath each other
+ _dffc_lay = layouter.inst_layout(inst=insts[f"dff_c[{n_bit}]"])
+ _dffc_bb = _dffc_lay.boundary
+ assert _dffc_bb is not None
+ _dffs_lay = layouter.inst_layout(
+ inst=insts[f"dff_s[{n_bit}]"], rotation=_geo.Rotation.MX,
+ )
+ _dffs_bb = _dffs_lay.boundary
+ assert _dffs_bb is not None
+
+ # Increase stdcell_left if n_byte is even but not 0;
+ # for zero we use the stdcell_left determined by the we flops
+ if mux_below and (n_byte != 0):
+ stdcell_left += dx_stdcell
+
+ if mux_below:
+ y_d = weffs_bb.bottom - _dffc_bb.top
+ else:
+ assert y_d is not None
+ y_d -= _dffc_bb.height + _dffs_bb.height
+
+ x = stdcell_left - _dffc_bb.left
+ y = y_d
+ dffc_lay = layouter.place(_dffc_lay, x=x, y=y)
+ dffc_bb = dffc_lay.boundary
+ assert dffc_bb is not None
+ dffcclk_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
+ dffcdc_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=d_c)
+ dffcds_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=d_s)
+ y = dffc_bb.bottom - _dffs_bb.top
+ dffs_lay = layouter.place(_dffs_lay, x=x, y=y)
+ dffsclk_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
+ dffsds_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=d_s)
+
+ # Connect d captureclk
+ net = nets.in_captureclk
+
+ o = dffcclk_lipinbb.center
+ mcondcclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mcondcclk_m1bb = mcondcclk_lay.bounds(mask=m1.mask)
+ if mux_below:
+ mcondcclk_m1bb1_last = mcondcclk_m1bb
+ else:
+ mcondcclk_m1bb2_last = mcondcclk_m1bb
+
+ # Connect d shifteclk
+ net = nets.in_shiftclk
+
+ o = dffsclk_lipinbb.center
+ mcondsclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mcondsclk_m1bb = mcondsclk_lay.bounds(mask=m1.mask)
+ if mux_below:
+ mcondsclk_m1bb1_last = mcondsclk_m1bb
+ else:
+ mcondsclk_m1bb2_last = mcondsclk_m1bb
+
+ # Connect d_c
+ _mcondc_lay = layouter.wire_layout(
+ net=d_c, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mcondc_libb = _mcondc_lay.bounds(mask=li.mask)
+
+ x = dffcdc_lipinbb.center.x
+ y = dffcdc_lipinbb.top - _mcondc_libb.top
+ mcondc_lay = layouter.place(_mcondc_lay, x=x, y=y)
+ mcondc_m1bb = mcondc_lay.bounds(mask=m1.mask)
+ x = sramd_m2pinbb.center.x
+ viadc_lay = layouter.add_wire(net=d_c, wire=via, rows=2, x=x, y=y)
+ viadc_m1bb = viadc_lay.bounds(mask=m1.mask)
+ viadc_m2bb = viadc_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(rect=sramd_m2pinbb, bottom=viadc_m2bb.bottom)
+ layouter.add_wire(net=d_c, wire=m2, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mcondc_m1bb, left=viadc_m1bb.left)
+ layouter.add_wire(net=d_c, wire=m1, shape=shape)
+
+ # Connect d_s
+ _mconds_lay = layouter.wire_layout(
+ net=d_s, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconds_libb = _mconds_lay.bounds(mask=li.mask)
+
+ x = dffsds_lipinbb.center.x
+ y = dffsds_lipinbb.top - _mconds_libb.top
+ mconds_lay1 = layouter.place(_mconds_lay, x=x, y=y)
+ mconds_m1bb1 = mconds_lay1.bounds(mask=m1.mask)
+
+ x = dffcds_lipinbb.center.x
+ y = dffcds_lipinbb.bottom - _mconds_libb.bottom
+ mconds_lay2 = layouter.place(_mconds_lay, x=x, y=y)
+ mconds_m1bb2 = mconds_lay2.bounds(mask=m1.mask)
+ if (not mux_below) and (n_bit < (self.word_size - 1)):
+ y = (
+ dffcds_lipinbb.top - _mconds_libb.top
+ - mconds_m1bb2.height - 2*m1.min_space
+ )
+ mconds_lay3 = layouter.place(_mconds_lay, x=x, y=y)
+ mconds_m1bb3 = mconds_lay3.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(rect=mconds_m1bb2, bottom=mconds_m1bb1.bottom)
+ layouter.add_wire(net=d_s, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconds_m1bb1, left=mconds_m1bb2.left)
+ layouter.add_wire(net=d_s, wire=m1, shape=shape)
+
+ # Connect previous output of input shiftregister
+ assert insreg_m1bb_prev is not None
+ assert insreg_net_prev is not None
+ dffssregp_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=insreg_net_prev)
+
+ _mconsregp_lay = layouter.wire_layout(
+ net=insreg_net_prev, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconsregp_libb = _mconsregp_lay.bounds(mask=li.mask)
+
+ x = dffssregp_lipinbb.center.x
+ y = dffssregp_lipinbb.bottom - _mconsregp_libb.bottom
+ mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
+ mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
+ if n_byte == 0:
+ # Straight line for we_s
+ viasregp_lay1 = layouter.add_wire(
+ net=insreg_net_prev, wire=via, rows=2,
+ origin=insreg_m1bb_prev.center,
+ )
+ viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
+
+ viasregp_lay2 = layouter.add_wire(
+ net=insreg_net_prev, wire=via, rows=2,
+ origin=mconsregp_m1bb.center,
+ )
+ viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
+ )
+ layouter.add_wire(net=insreg_net_prev, wire=m2, shape=shape)
+ elif not mux_below:
+ # Horizontal m1, vertical m2
+ viasregp_lay1 = layouter.add_wire(
+ net=insreg_net_prev, wire=via, rows=2,
+ origin=insreg_m1bb_prev.center,
+ )
+ viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
+
+ x = insreg_m1bb_prev.center.x
+ y = mconsregp_m1bb.center.y
+ viasregp_lay2 = layouter.add_wire(
+ net=insreg_net_prev, wire=via, rows=2, x=x, y=y,
+ )
+ viasregp_m1bb2 = viasregp_lay2.bounds(mask=m1.mask)
+ viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=mconsregp_m1bb, right=viasregp_m1bb2.right,
+ )
+ layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(
+ rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
+ )
+ layouter.add_wire(net=insreg_net_prev, wire=m2, shape=shape)
+ else: # mux_below and n_byte > 0
+ # Connect from previous column with a polygon on m1
+ left = stdcell_left
+ right = left + insreg_m1bb_prev.height
+ shape = _geo.Polygon.from_floats(points=(
+ (insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
+ (insreg_m1bb_prev.left, insreg_m1bb_prev.top),
+ (left, insreg_m1bb_prev.top),
+ (left, mconsregp_m1bb.top),
+ (mconsregp_m1bb.right, mconsregp_m1bb.top),
+ (mconsregp_m1bb.right, mconsregp_m1bb.bottom),
+ (right, mconsregp_m1bb.bottom),
+ (right, insreg_m1bb_prev.bottom),
+ (insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
+ ))
+ layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
+ if mux_below or (n_bit == (self.word_size - 1)):
+ insreg_m1bb_prev = mconds_m1bb1
+ else:
+ insreg_m1bb_prev = mconds_m1bb3
+ insreg_net_prev = d_s
+
+ # Place the q flops, put two underneath each other
+ if mux_below:
+ _qffc_lay = layouter.inst_layout(
+ inst=insts[f"qff_c[{n_bit}]"],
+ )
+ _qffc_bb = _qffc_lay.boundary
+ assert _qffc_bb is not None
+ _qffs_lay = layouter.inst_layout(
+ inst=insts[f"qff_s[{n_bit}]"], rotation=_geo.Rotation.MX,
+ )
+ _qffs_bb = _qffs_lay.boundary
+ assert _qffs_bb is not None
+ _qmux_lay = layouter.inst_layout(
+ inst=insts[f"qmux[{n_bit}]"],
+ )
+ _qmux_bb = _qmux_lay.boundary
+ assert _qmux_bb is not None
+
+ y_q = y_d - 4*_qffc_bb.height
+ x_mux = stdcell_left - _qmux_bb.left
+ y_mux = y_q - 2*_qffc_bb.height
+ else:
+ _qffc_lay = layouter.inst_layout(
+ inst=insts[f"qff_c[{n_bit}]"], rotation=_geo.Rotation.MX,
+ )
+ _qffc_bb = _qffc_lay.boundary
+ assert _qffc_bb is not None
+ _qffs_lay = layouter.inst_layout(
+ inst=insts[f"qff_s[{n_bit}]"],
+ )
+ _qffs_bb = _qffs_lay.boundary
+ assert _qffs_bb is not None
+ _qmux_lay = layouter.inst_layout(
+ inst=insts[f"qmux[{n_bit}]"],
+ )
+ _qmux_bb = _qmux_lay.boundary
+ assert _qmux_bb is not None
+
+ x_mux = stdcell_left - _qmux_bb.left + _qmux_bb.width
+ y_mux = y_d - 4*_qffc_bb.height
+ y_q = y_mux
+
+ x = stdcell_left - _qffc_bb.left
+ y = y_q
+ qffc_lay = layouter.place(_qffc_lay, x=x, y=y)
+ qffc_bb = qffc_lay.boundary
+ assert qffc_bb is not None
+ qffcclk_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=nets.out_captureclk)
+ qffcqc_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=q_c)
+ qffcqcl_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=q_cl)
+ y = qffc_bb.bottom - _qffs_bb.top
+ qffs_lay = layouter.place(_qffs_lay, x=x, y=y)
+ qffsclk_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=nets.out_shiftclk)
+ qffsqs_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=q_s)
+ qffsqmux_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=q_mux)
+ qmux_lay = layouter.place(_qmux_lay, x=x_mux, y=y_mux)
+ qmuxqmux_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=q_mux)
+ qmuxqcl_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=q_cl)
+ qmuxdoc_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=nets.out_docapture)
+
+ # Connect q captureclk
+ net = nets.out_captureclk
+
+ o = qffcclk_lipinbb.center
+ mconqcclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mconqcclk_m1bb = mconqcclk_lay.bounds(mask=m1.mask)
+ if mux_below:
+ mconqcclk_m1bb1_last = mconqcclk_m1bb
+ else:
+ mconqcclk_m1bb2_last = mconqcclk_m1bb
+
+ # Connect q shifteclk
+ net = nets.out_shiftclk
+
+ o = qffsclk_lipinbb.center
+ mconqsclk_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mconqsclk_m1bb = mconqsclk_lay.bounds(mask=m1.mask)
+ if mux_below:
+ mconqsclk_m1bb1_last = mconqsclk_m1bb
+ else:
+ mconqsclk_m1bb2_last = mconqsclk_m1bb
+
+ # Connect q_c
+ net = q_c
+
+ _mconqc_lay = layouter.wire_layout(
+ net=net, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconqc_libb = _mconqc_lay.bounds(mask=li.mask)
+
+ x = qffcqc_lipinbb.center.x
+ y = qffcqc_lipinbb.top - _mconqc_libb.top
+ mconqc_lay = layouter.place(_mconqc_lay, x=x, y=y)
+ mconqc_m1bb = mconqc_lay.bounds(mask=m1.mask)
+ x = sramq_m2pinbb.center.x
+ viaqc_lay = layouter.add_wire(net=net, wire=via, rows=2, x=x, y=y)
+ viaqc_m1bb = viaqc_lay.bounds(mask=m1.mask)
+ viaqc_m2bb = viaqc_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(rect=sramq_m2pinbb, bottom=viaqc_m2bb.bottom)
+ layouter.add_wire(net=net, wire=m2, shape=shape)
+ if (mconqc_m1bb.left > viaqc_m1bb.left):
+ shape = _geo.Rect.from_rect(rect=mconqc_m1bb, left=viaqc_m1bb.left)
+ else:
+ shape = _geo.Rect.from_rect(rect=mconqc_m1bb, right=viaqc_m1bb.right)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ # Connect q_cl
+ net = q_cl
+
+ _mconqcl_lay = layouter.wire_layout(
+ net=net, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconqcl_libb = _mconqcl_lay.bounds(mask=li.mask)
+
+ x = qffcqcl_lipinbb.center.x
+ if mux_below:
+ y = qffcqcl_lipinbb.bottom - _mconqcl_libb.bottom
+ else:
+ y = qffcqcl_lipinbb.top - _mconqcl_libb.top
+ mconqcl_lay = layouter.place(_mconqcl_lay, x=x, y=y)
+ mconqcl_m1bb = mconqcl_lay.bounds(mask=m1.mask)
+
+ x = qmuxqcl_lipinbb.center.x
+ viaqcl_lay1 = layouter.add_wire(
+ net=net, wire=via, rows=2, x=x, y=y,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ viaqcl_m1bb1 = viaqcl_lay1.bounds(mask=m1.mask)
+ viaqcl_m2bb1 = viaqcl_lay1.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=mconqcl_m1bb, left = viaqcl_m1bb1.left,
+ )
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ x = qmuxqcl_lipinbb.center.x
+ if mux_below:
+ y = qmuxqcl_lipinbb.top - _mconqcl_libb.top
+ else:
+ y = qmuxqcl_lipinbb.bottom - _mconqcl_libb.bottom
+ # Use second row, i0 has already higher bottom
+ y += 2*m1.min_space
+ layouter.place(_mconqcl_lay, x=x, y=y)
+ viaqcl_lay2 = layouter.add_wire(
+ net=net, wire=via, rows=2, x=x, y=y,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ viaqcl_m2bb2 = viaqcl_lay2.bounds(mask=m2.mask)
+
+ if mux_below:
+ shape = _geo.Rect.from_rect(
+ rect=viaqcl_m2bb1, bottom=viaqcl_m2bb2.bottom,
+ )
+ else:
+ shape = _geo.Rect.from_rect(
+ rect=viaqcl_m2bb1, top=viaqcl_m2bb2.top,
+ )
+ layouter.add_wire(net=net, wire=m2, shape=shape)
+
+ # Connect q_s
+ _mconqs_lay = layouter.wire_layout(
+ net=q_s, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconqs_libb = _mconqs_lay.bounds(mask=li.mask)
+
+ x = qffsqs_lipinbb.center.x
+ if mux_below:
+ y = qffsqs_lipinbb.bottom - _mconqs_libb.bottom
+ else:
+ y = qffsqs_lipinbb.top - _mconqs_libb.top
+ mconqs_lay1 = layouter.place(_mconqs_lay, x=x, y=y)
+ mconqs_m1bb1 = mconqs_lay1.bounds(mask=m1.mask)
+
+ # Connect out_docapture
+ net = nets.out_docapture
+ o = self.tech.on_grid(qmuxdoc_lipinbb.center, mult=2)
+ mcondoc_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ mcondoc_m1bb = mcondoc_lay.bounds(mask=m1.mask)
+
+ if n_bit == 0:
+ mcondoc_m1bb_first = mcondoc_m1bb
+ mcondoc_m1bb_last = mcondc_m1bb
+
+ # Connect q_mux
+ _mconqmux_lay = layouter.wire_layout(
+ net=q_mux, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconqmux_libb = _mconqmux_lay.bounds(mask=li.mask)
+
+ x = qffsqmux_lipinbb.center.x
+ if mux_below:
+ y = qffsqmux_lipinbb.bottom - _mconqmux_libb.bottom
+ else:
+ y = qffsqmux_lipinbb.top - _mconqmux_libb.top
+ mconqmux_lay1 = layouter.place(_mconqmux_lay, x=x, y=y)
+ mconqmux_m1bb1 = mconqmux_lay1.bounds(mask=m1.mask)
+
+ x = qmuxqmux_lipinbb.center.x
+ if mux_below:
+ y = qmuxqmux_lipinbb.top - _mconqmux_libb.top
+ else:
+ y = qmuxqmux_lipinbb.bottom - _mconqmux_libb.bottom
+ mconqmux_lay2 = layouter.place(_mconqmux_lay, x=x, y=y)
+ mconqmux_m1bb2 = mconqmux_lay2.bounds(mask=m1.mask)
+
+ assert mconqmux_m1bb1.left < mconqmux_m1bb2.left
+ if mux_below:
+ # Connect on m1 with polygon
+ shape = _geo.Polygon.from_floats(points=(
+ (mconqmux_m1bb1.left, mconqmux_m1bb2.bottom),
+ (mconqmux_m1bb1.left, mconqmux_m1bb1.top),
+ (mconqmux_m1bb1.right, mconqmux_m1bb1.top),
+ (mconqmux_m1bb1.right, mconqmux_m1bb2.top),
+ (mconqmux_m1bb2.right, mconqmux_m1bb2.top),
+ (mconqmux_m1bb2.right, mconqmux_m1bb2.bottom),
+ (mconqmux_m1bb1.left, mconqmux_m1bb2.bottom),
+ ))
+ layouter.add_wire(net=q_mux, wire=m1, shape=shape)
+ else:
+ # Horizontal m1, vertical m2
+ shape = _geo.Rect.from_rect(
+ rect=mconqmux_m1bb2, left=mconqmux_m1bb1.left,
+ )
+ layouter.add_wire(net=q_mux, wire=m1, shape=shape)
+
+ x = mconqmux_m1bb1.center.x
+ y = mconqmux_m1bb2.center.y
+ viaqmux_lay1 = layouter.add_wire(
+ net=q_mux, wire=via, rows=2, x=x, y=y,
+ bottom_enclosure="tall", top_enclosure="tall",
+ )
+ viaqmux_m2bb1 = viaqmux_lay1.bounds(mask=m2.mask)
+
+ o = mconqmux_m1bb1.center
+ viaqmux_lay2 = layouter.add_wire(
+ net=q_mux, wire=via, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="tall",
+ )
+ viaqmux_m2bb2 = viaqmux_lay2.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=viaqmux_m2bb1, bottom=viaqmux_m2bb2.bottom,
+ )
+ layouter.add_wire(net=q_mux, wire=m2, shape=shape)
+
+ # Connect previous output of out shiftregister
+ if outsreg_m1bb_prev is None:
+ assert outsreg_net_prev is None
+ assert n_byte == 0 # Can only happen for first bit in byte
+ outsreg_net_first = net = nets[f"d_s[{self.word_size - 1}]"]
+ qmuxds_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=net)
+
+ _mconds_lay = layouter.wire_layout(
+ net=net, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconds_libb = _mconds_lay.bounds(mask=li.mask)
+
+ x = qmuxds_lipinbb.center.x
+ y = (
+ qmuxds_lipinbb.top - _mconds_libb.top
+ - _mconds_libb.height - 2*m1.min_space
+ )
+ mconds_lay = layouter.place(_mconds_lay, x=x, y=y)
+ outsreg_m1bb_first = mconds_lay.bounds(mask=m1.mask)
+ else:
+ assert outsreg_net_prev is not None
+ qmuxsregp_lipinbb = qmux_lay.bounds(
+ mask=lipin.mask, net=outsreg_net_prev,
+ )
+
+ _mconsregp_lay = layouter.wire_layout(
+ net=insreg_net_prev, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconsregp_libb = _mconsregp_lay.bounds(mask=li.mask)
+
+ if mux_below:
+ x = qmuxsregp_lipinbb.center.x
+ y = (
+ qmuxsregp_lipinbb.bottom - _mconsregp_libb.bottom
+ + _mconsregp_libb.height + 2*m1.min_space
+ )
+ mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
+ mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
+
+ x = outsreg_m1bb_prev.center.x
+ viasregp_lay1 = layouter.add_wire(
+ net=outsreg_net_prev, wire=via, rows=2, x=x, y=y,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ viasregp_m1bb1 = viasregp_lay1.bounds(mask=m1.mask)
+ viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=mconsregp_m1bb, left=viasregp_m1bb1.left,
+ )
+ layouter.add_wire(
+ net=outsreg_net_prev, wire=m1, shape=shape,
+ )
+ y = outsreg_m1bb_prev.center.y
+ viasregp_lay2 = layouter.add_wire(
+ net=outsreg_net_prev, wire=via, rows=2, x=x, y=y,
+ bottom_enclosure="wide", top_enclosure="tall",
+ )
+ viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
+ )
+ layouter.add_wire(net=outsreg_net_prev, wire=m2, shape=shape)
+ else:
+ x = qmuxsregp_lipinbb.center.x
+ y = qmuxsregp_lipinbb.top - _mconsregp_libb.top
+ mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
+ mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Polygon.from_floats(points=(
+ (mconsregp_m1bb.left, mconsregp_m1bb.bottom),
+ (mconsregp_m1bb.left, outsreg_m1bb_prev.top),
+ (outsreg_m1bb_prev.right, outsreg_m1bb_prev.top),
+ (outsreg_m1bb_prev.right, outsreg_m1bb_prev.bottom),
+ (mconsregp_m1bb.right, outsreg_m1bb_prev.bottom),
+ (mconsregp_m1bb.right, mconsregp_m1bb.bottom),
+ (mconsregp_m1bb.left, mconsregp_m1bb.bottom),
+ ))
+ layouter.add_wire(net=outsreg_net_prev, wire=m1, shape=shape)
+ outsreg_m1bb_prev = mconqs_m1bb1
+ outsreg_net_prev = q_s
+
+ # Place the out captureclk flop if the first bit
+ if n_bit == 0:
+ _cclkff_lay = layouter.inst_layout(inst=insts.outcaptclkff)
+ _cclkff_bb = _cclkff_lay.boundary
+ assert _cclkff_bb is not None
+
+ x = qffc_bb.left - _cclkff_bb.right
+ y = qffc_bb.bottom - _cclkff_bb.bottom
+ cclkff_lay = layouter.place(_cclkff_lay, x=x, y=y)
+ cclkffclk_lipinbb = cclkff_lay.bounds(
+ mask=lipin.mask, net=nets.sramclk,
+ )
+ cclkffcclk_lipinbb = cclkff_lay.bounds(
+ mask=lipin.mask, net=nets.out_captureclk,
+ )
+ cclkffcclkl_lipinbb = cclkff_lay.bounds(
+ mask=lipin.mask, net=nets.out_captureclk_l,
+ )
+
+ # Connedt sramclk
+ net = nets.sramclk
+
+ _mconclk_lay = layouter.wire_layout(
+ net=net, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconclk_libb = _mconclk_lay.bounds(mask=li.mask)
+
+ x = cclkffclk_lipinbb.center.x
+ y = cclkffclk_lipinbb.bottom - _mconclk_libb.bottom
+ mconclk_lay = layouter.place(_mconclk_lay, x=x, y=y)
+ mconclk_m1bb = mconclk_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=mconclk_m1bb, left=sramclk_m2pinbb.left,
+ )
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ x = sramclk_m2pinbb.center.x
+ y = mconclk_m1bb.center.y
+ viaclk_lay = layouter.add_wire(
+ net=net, wire=via, x=x, y=y,
+ top_width=sramclk_m2pinbb.width,
+ )
+ viaclk_m2bb = viaclk_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(
+ rect=viaclk_m2bb, top=sramclk_m2pinbb.top
+ )
+ layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
+
+ # Connect out_captureclk
+ net = nets.out_captureclk
+
+ o = cclkffcclk_lipinbb.center
+ layouter.add_wire(
+ net=net, wire=mcon, rows=2, origin=o,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+
+ # Connect out_captureclk_l
+ net = nets.out_captureclk_l
+
+ _mconcclkl_lay = layouter.wire_layout(
+ net=net, wire=mcon, rows=2,
+ bottom_enclosure="tall", top_enclosure="wide",
+ )
+ _mconcclkl_libb = _mconcclkl_lay.bounds(mask=li.mask)
+
+ x = cclkffcclkl_lipinbb.center.x
+ y = cclkffcclkl_lipinbb.bottom - _mconcclkl_libb.bottom
+ mconcclkl_lay = layouter.place(_mconcclkl_lay, x=x, y=y)
+ mconcclkl_m1bb = mconcclkl_lay.bounds(mask=m1.mask)
+
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=mconcclkl_m1bb)
+
+ # Connect output of in shift register to first of output shift register
+ assert insreg_m1bb_prev is not None
+ assert insreg_net_prev is not None
+ assert outsreg_m1bb_first is not None
+ assert outsreg_net_first is not None
+ assert insreg_net_prev == outsreg_net_first
+
+ left = insreg_m1bb_prev.right + 2*m1.min_space
+ right = left + insreg_m1bb_prev.height
+ shape = _geo.Polygon.from_floats(points=(
+ (outsreg_m1bb_first.left, outsreg_m1bb_first.bottom),
+ (outsreg_m1bb_first.left, outsreg_m1bb_first.top),
+ (left, outsreg_m1bb_first.top),
+ (left, insreg_m1bb_prev.bottom),
+ (insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
+ (insreg_m1bb_prev.left, insreg_m1bb_prev.top),
+ (right, insreg_m1bb_prev.top),
+ (right, outsreg_m1bb_first.bottom),
+ (outsreg_m1bb_first.left, outsreg_m1bb_first.bottom),
+ ))
+ layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
+ assert mconwecclk_m1bb_last is not None
+ assert mconwesclk_m1bb_last is not None
+ assert mcondcclk_m1bb1_last is not None
+ assert mcondcclk_m1bb2_last is not None
+ assert mcondsclk_m1bb1_last is not None
+ assert mcondsclk_m1bb2_last is not None
+ assert mconqcclk_m1bb1_last is not None
+ assert mconqcclk_m1bb2_last is not None
+ assert mconqsclk_m1bb1_last is not None
+ assert mconqsclk_m1bb2_last is not None
+
+ # Connect shift_out
+ assert outsreg_m1bb_prev is not None
+ assert outsreg_net_prev is not None
+ assert outsreg_net_prev == nets.shift_out
+ layouter.add_wire(
+ net=outsreg_net_prev, wire=m1, pin=m1pin, shape=outsreg_m1bb_prev,
+ )
+
+ # Conect in_captureclk
+ net = nets.in_captureclk
+
+ bottom = mconwecclk_m1bb_last.bottom
+ left = mconacclk_m1bb_last.left
+ shape = _geo.Rect.from_rect(rect=mconacclk_m1bb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconwecclk_m1bb_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mcondcclk_m1bb1_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mcondcclk_m1bb2_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ x = mconacclk_m1bb_last.center.x
+ y = mconwecclk_m1bb_last.center.y
+ via_lay = layouter.add_wire(
+ net=net, wire=via, rows=2, columns=2, x=x, y=y,
+ )
+ via_m2bb1 = via_lay.bounds(mask=m2.mask)
+ y = mcondcclk_m1bb1_last.center.y
+ layouter.add_wire(
+ net=net, wire=via, rows=2, columns=2, x=x, y=y,
+ )
+ y = mcondcclk_m1bb2_last.center.y
+ via_lay = layouter.add_wire(
+ net=net, wire=via, rows=2, columns=2, x=x, y=y,
+ )
+ via_m2bb2 = via_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(rect=via_m2bb1, bottom=via_m2bb2.bottom)
+ layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
+
+ # Conect in_shiftclk
+ net = nets.in_shiftclk
+
+ bottom = mcondsclk_m1bb2_last.bottom
+ left = mconasclk_m1bb_last.left
+ shape = _geo.Rect.from_rect(rect=mconasclk_m1bb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconwesclk_m1bb_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mcondsclk_m1bb1_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mcondsclk_m1bb2_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ # Conect out_captureclk
+ net = nets.out_captureclk
+
+ left = 5.0
+ shape = _geo.Rect.from_rect(rect=mconqcclk_m1bb1_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconqcclk_m1bb2_last, left=left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ right = 6.0
+ shape = _geo.Rect(
+ left=left, bottom=mconqcclk_m1bb2_last.bottom,
+ right=right, top=mconqcclk_m1bb1_last.top,
+ )
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
+
+ # Conect out_shiftclk
+ net = nets.out_shiftclk
+
+ x = 27.0
+ shape = _geo.Rect.from_rect(rect=mconqsclk_m1bb1_last, left=x)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+ shape = _geo.Rect.from_rect(rect=mconqsclk_m1bb2_last, left=x)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ y = mconqsclk_m1bb1_last.center.y
+ via_lay = layouter.add_wire(
+ net=net, wire=via, rows=2, columns=2, x=x, y=y,
+ )
+ via_m2bb1 = via_lay.bounds(mask=m2.mask)
+ y = mconqsclk_m1bb2_last.center.y
+ via_lay = layouter.add_wire(
+ net=net, wire=via, rows=2, columns=2, x=x, y=y,
+ )
+ via_m2bb2 = via_lay.bounds(mask=m2.mask)
+
+ shape = _geo.Rect.from_rect(rect=via_m2bb1, bottom=via_m2bb2.bottom)
+ layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
+
+ # Connect out_docapture
+ net = nets.out_docapture
+
+ assert mcondoc_m1bb_first is not None
+ assert mcondoc_m1bb_last is not None
+ shape = _geo.Rect.from_rect(
+ rect=mcondoc_m1bb_first, right=mcondoc_m1bb_last.right,
+ )
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
+
+ # TODO: remove hard coded values
+ stdcell_height = 10.0
+ stdcellvss_height = 1.2
+ stdcellvdd_height = 1.2
+ stdcellnwelledge_height = 4.8
+
+ bottomblock_top = -5
+
+ # Connect dvss
+ net = nets.dvss
+
+ bottom = -115
+ shape = _geo.Rect.from_rect(rect=affsvss_lipinbb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+ shape = _geo.Rect.from_rect(rect=affcvss_lipinbb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+
+ left = affcvss_lipinbb_last.left
+ right = sram_bb.right
+ for i in range(6):
+ top = bottomblock_top - (2*i + 1)*stdcell_height + stdcellvss_height
+ if i < 5:
+ bottom = top - 2*stdcellvss_height
+ else:
+ bottom = top - stdcellvss_height
+ shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+
+ # Connect with first column
+ if i == 4:
+ x = affcvss_lipinbb_last.left
+ y = 0.5*(bottom + top)
+ mcon_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=7, columns=7, x=x, y=y
+ )
+ mcon_m1bb1 = mcon_lay.bounds(mask=m1.mask)
+ x = affsvss_lipinbb_last.center.x
+ mcon_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=7, columns=7, x=x, y=y
+ )
+ mcon_m1bb2 = mcon_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(rect=mcon_m1bb1, left=mcon_m1bb2.left)
+ layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
+
+ # Connect dvdd
+ net = nets.dvdd
+
+ bottom = -115
+ shape = _geo.Rect.from_rect(rect=affsvdd_lipinbb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+ shape = _geo.Rect.from_rect(rect=affcvdd_lipinbb_last, bottom=bottom)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+
+ left = sram_bb.left
+ right = sram_bb.right
+ for i in range(6):
+ # li
+ bottom = bottomblock_top - 2*i*stdcell_height - stdcellvdd_height
+ if i == 0:
+ top = bottom + stdcellvdd_height
+ else:
+ top = bottom + 2*stdcellvdd_height
+ shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
+ layouter.add_wire(net=net, wire=li, shape=shape)
+
+ # nwell
+ bottom = bottomblock_top - (2*i + 1)*stdcell_height + stdcellnwelledge_height
+ if i == 0:
+ top = bottomblock_top + 1.0
+ else:
+ top = bottomblock_top - (2*i - 1)*stdcell_height - stdcellnwelledge_height
+ shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
+ layouter.add_wire(net=net, wire=nwm, shape=shape)
+
+ x = left + 1.0
+ y = 0.5*(bottom + top)
+ mcon_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=7, columns=7, x=x, y=y
+ )
+ mcon_m1bb1 = mcon_lay.bounds(mask=m1.mask)
+ via_lay = layouter.add_wire(
+ net=net, wire=via, rows=4, columns=4, x=x, y=y,
+ )
+ via_m2bb = via_lay.bounds(mask=m2.mask)
+ if i == 5:
+ # Connect with column
+ x = affsvdd_lipinbb_last.right
+ mcon_lay = layouter.add_wire(
+ net=net, wire=mcon, rows=7, columns=7, x=x, y=y
+ )
+ mcon_m1bb2 = mcon_lay.bounds(mask=m1.mask)
+
+ shape = _geo.Rect.from_rect(rect=mcon_m1bb1, left=mcon_m1bb2.left)
+ layouter.add_wire(net=net, wire=m1, shape=shape)
+
+ shape = _geo.Rect.from_rect(rect=via_m2bb, top=bottomblock_top)
+ layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
+
+ # Boundary
+ layouter.layout.boundary = _geo.Rect(
+ left=affsvss_lipinbb_last.left,
+ bottom=(bottomblock_top - 11*stdcell_height),
+ right = sram_bb.right,
+ top = sram_bb.top,
+ )
+
class ConnectedSRAM(_lbry._Cell[_lbry.Library]):
def __init__(self, *, lib: _lbry.Library):
super().__init__(lib=lib, name="ConnectedSRAM")
@@ -160,7 +1572,6 @@
layouter = self.new_circuitlayouter()
layout = layouter.layout
-
# vss/vdd for the included standard cells
#
dvss_name = "vssd1"
@@ -171,7 +1582,6 @@
dvdd = ckt.new_net(name=dvdd_name, external=True)
dvdd_bb = _frm.toppins[dvdd_name]
-
# Place the SRAM
#
# instantiate
@@ -196,6 +1606,14 @@
dpsram_bb = dpsram_lay.boundary
assert dpsram_bb is not None
+ # Temporary add
+ spchar_cell = SPCharacterizationWrapper(lib=lib, spcell=spsram_cell)
+ spchar = ckt.instantiate(spchar_cell, name="sramchar")
+ x = spsram_bb.right + 700
+ spchar_lay = layouter.place(
+ spchar, x=x, y=(y + 50), rotation=_geo.Rotation.R90,
+ )
+
# Make three rows of to place standard cells in
#
dbound = 4.0
diff --git a/gds/user_analog_project_wrapper.gds.gz b/gds/user_analog_project_wrapper.gds.gz
index ca5b026..dc51ee5 100644
--- a/gds/user_analog_project_wrapper.gds.gz
+++ b/gds/user_analog_project_wrapper.gds.gz
Binary files differ