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