Add 3.3V bandgap.
diff --git a/doitcode/bandgap.py b/doitcode/bandgap.py
new file mode 100644
index 0000000..3176dfc
--- /dev/null
+++ b/doitcode/bandgap.py
@@ -0,0 +1,747 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+import sys
+from termios import TIOCPKT_FLUSHREAD
+from typing import List, Tuple, cast
+
+from pdkmaster.technology import geometry as _geo, primitive as _prm
+from pdkmaster.design import circuit as _ckt, layout as _lay, library as _lbry
+
+from c4m.pdk import sky130
+_prims = sky130.tech.primitives
+
+from . import frame as _frm
+
+
+__all__ = ["ConnectedBandGap"]
+
+
+class _BandGap3V3(_lbry._OnDemandCell[_lbry.Library]):
+    def __init__(self, *, lib: _lbry.Library):
+        super().__init__(lib=lib, name="BandGap3V3")
+
+    def _create_circuit(self):
+        ckt = self.new_circuit()
+
+        ln = 2.0
+        wn = 3.7
+        lp  = 2.0
+        wp = 18
+
+        res1_h = 11.5
+        res2_h = 20.0
+        self.res2_n = res2_n = 7
+
+        self.pnp_ratio = pnp_ratio = 4
+
+        nmos = cast(_prm.MOSFET, _prims.nfet_g5v0d10v5)
+        pmos = cast(_prm.MOSFET, _prims.pfet_g5v0d10v5)
+        res = cast(_prm.Resistor, _prims.poly_res)
+
+        p1 = ckt.instantiate(pmos, name="p1", l=lp, w=wp)
+        p2 = ckt.instantiate(pmos, name="p2", l=lp, w=wp)
+        p3 = ckt.instantiate(pmos, name="p3", l=lp, w=wp)
+        ps = (p1, p2, p3)
+
+        n1 = ckt.instantiate(nmos, name="n1", l=ln, w=wn)
+        n2 = ckt.instantiate(nmos, name="n2", l=ln, w=wn)
+        ns = (n1, n2)
+
+        res1 = ckt.instantiate(res, name="res1", height=res1_h)
+        res2s = tuple(
+            ckt.instantiate(res, name=f"res2[{n}]", height=res2_h)
+            for n in range(res2_n)
+        )
+
+        pnp_cell = sky130.macrolib.cells["PNP_05v5_W3u40L3u40"]
+        pnp1 = ckt.instantiate(pnp_cell, name="pnp1")
+        pnp2s = tuple(
+            ckt.instantiate(pnp_cell, name=f"pnp2[{n}]")
+            for n in range(pnp_ratio)
+        )
+        pnp3s = tuple(
+            ckt.instantiate(pnp_cell, name=f"pnp3[{n}]")
+            for n in range(pnp_ratio)
+        )
+        pnps = (pnp1, *pnp2s, *pnp3s)
+
+        ckt.new_net(name="vss", external=True, childports=(
+            *(n.ports.bulk for n in ns),
+            *(pnp.ports.base for pnp in pnps),
+            *(pnp.ports.collector for pnp in pnps),
+        ))
+        ckt.new_net(name="vdd", external=True, childports=(
+            *(p.ports.sourcedrain1 for p in ps),
+            *(p.ports.bulk for p in ps),
+        ))
+        ckt.new_net(name="vref", external=True, childports=(
+            p3.ports.sourcedrain2, res2s[-1].ports.port2,
+        ))
+
+        ckt.new_net(name="p_gate", external=False, childports=(
+            *(p.ports.gate for p in ps),
+            p2.ports.sourcedrain2, n2.ports.sourcedrain2,
+        ))
+        ckt.new_net(name="n_gate", external=False, childports=(
+            *(n.ports.gate for n in ns),
+            n1.ports.sourcedrain2, p1.ports.sourcedrain2,
+        ))
+        ckt.new_net(name="vq1", external=False, childports=(
+            pnp1.ports.emitter, n1.ports.sourcedrain1,
+        ))
+        ckt.new_net(name="vq2", external=False, childports=(
+            *(pnp2.ports.emitter for pnp2 in pnp2s), res1.ports.port1,
+        ))
+        ckt.new_net(name="vq2r1", external=False, childports=(
+            res1.ports.port2, n2.ports.sourcedrain1,
+        ))
+        ckt.new_net(name="vq3", external=False, childports=(
+            *(pnp3.ports.emitter for pnp3 in pnp3s), res2s[0].ports.port1,
+        ))
+
+        for n in range(res2_n - 1):
+            res2_1 = res2s[n]
+            res2_2 = res2s[n + 1]
+            ckt.new_net(name=f"res2_{n}", external=False, childports=(
+                res2_1.ports.port2, res2_2.ports.port1,
+            ))
+
+    def _create_layout(self):
+        tech = self.tech
+
+        # We don't try to optimize area for this circuit yet.
+        ckt = self.circuit
+        nets = ckt.nets
+        insts = ckt.instances
+
+        nwm = cast(_prm.Well, _prims.nwm)
+        difftap = cast(_prm.WaferWire, _prims.difftap)
+        nsdm = cast(_prm.Implant, _prims.nsdm)
+        psdm = cast(_prm.Implant, _prims.psdm)
+        hvi = cast(_prm.Insulator, _prims.hvi)
+        poly = cast(_prm.GateWire, _prims.poly)
+        licon = cast(_prm.Via, _prims.licon)
+        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]
+        via1 = cast(_prm.Via, _prims.via)
+        m2 = cast(_prm.MetalWire, _prims.m2)
+        assert m2.pin is not None
+        bnd = cast(_prm.Auxiliary, _prims.prBoundary)
+
+        layouter = self.new_circuitlayouter()
+        layout = layouter.layout
+
+        # place res1
+        _res1 = layouter.inst_layout(inst=insts.res1, rotation=_geo.Rotation.MX)
+        _bb = _res1.bounds()
+        x = -1.0 - _bb.right
+        y = 20.0 - _bb.top
+        res1_lay = layouter.place(insts.res1, x=x, y=y)
+        res1vq2_libb = res1_lay.bounds(net=nets.vq2, mask=li.mask)
+        res1vq2r1_libb = res1_lay.bounds(net=nets.vq2r1, mask=li.mask)
+
+        # place bipolars
+        pnp1 = cast(_ckt._CellInstance, insts.pnp1)
+        pnp2s = tuple(
+            cast(_ckt._CellInstance, insts[f"pnp2[{n}]"])
+            for n in range(self.pnp_ratio)
+        )
+        pnp3s = tuple(
+            cast(_ckt._CellInstance, insts[f"pnp3[{n}]"])
+            for n in range(self.pnp_ratio)
+        )
+
+        _pnp1_bb = pnp1.cell.layout.boundary
+        assert _pnp1_bb is not None
+
+        x0_pnp = -_pnp1_bb.left
+        y0_pnp = -_pnp1_bb.bottom
+        dx_pnp = _pnp1_bb.width
+        dy_pnp = _pnp1_bb.height
+
+        assert self.pnp_ratio == 4, "Internal error"
+        inst_matrix = (
+            (pnp2s[0], pnp2s[1], pnp3s[0]),
+            (pnp3s[1], pnp1, pnp3s[2]),
+            (pnp3s[3], pnp2s[2], pnp2s[3]),
+        )
+        lay_matrix: List[Tuple[_lay._Layout, _lay._Layout, _lay._Layout]] = []
+        for row in range(3):
+            row_lays: List[_lay._Layout] = []
+            y = y0_pnp + row*dy_pnp
+            for col in range(3):
+                x = x0_pnp + col*dx_pnp
+                inst = inst_matrix[row][col]
+                row_lays.append(layouter.place(inst, x=x, y=y))
+            lay_matrix.append(tuple(row_lays))
+
+        # vss connection to the bipolars
+        right = 0.0
+        top = 0.0
+        for ms in lay_matrix[0][0].filter_polygons(
+            net=nets.vss, mask=lipin.mask, split=True
+        ):
+            shape = ms.shape
+            right = max(right, shape.bounds.left)
+            top = max(top, shape.bounds.bottom)
+
+        shape = _geo.Rect(left=0.0, bottom=0.0, right=3*dx_pnp, top=top)
+        layouter.add_wire(net=nets.vss, wire=li, pin=lipin, shape=shape)
+        shape = _geo.Rect(
+            left=0.0, bottom=(dy_pnp - top), right=3*dx_pnp, top=(dy_pnp + top),
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+        shape = _geo.Rect(
+            left=0.0, bottom=(2*dy_pnp - top), right=3*dx_pnp, top=(2*dy_pnp + top),
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+        shape = _geo.Rect(
+            left=0.0, bottom=(3*dy_pnp - top), right=3*dx_pnp, top=3*dy_pnp,
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+
+        shape = _geo.Rect(left=0.0, bottom=0.0, right=right, top=3*dy_pnp)
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+        shape = _geo.Rect(
+            left=(dx_pnp - right), bottom=0.0, right=(dx_pnp + right), top=3*dy_pnp,
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+        shape = _geo.Rect(
+            left=(2*dx_pnp - right), bottom=0.0, right=(2*dx_pnp + right), top=3*dy_pnp,
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+        shape = _geo.Rect(
+            left=(3*dx_pnp - right), bottom=0.0, right=3*dx_pnp, top=3*dy_pnp,
+        )
+        layouter.add_wire(net=nets.vss, wire=li, shape=shape)
+
+        # Place res2s
+        polyport2bb = liport2bb = None
+        x = 3*dx_pnp + 1.0
+        vq3res2_libb = None
+        vrefres2_libb = None
+        for n in range(self.res2_n):
+            if (n%2) == 0:
+                _res_lay = layouter.inst_layout(inst=insts[f"res2[{n}]"])
+            else:
+                _res_lay = layouter.inst_layout(
+                    inst=insts[f"res2[{n}]"], rotation=_geo.Rotation.MX,
+                )
+            _res_polybb = _res_lay.bounds(mask=poly.mask)
+
+            x_res = x - _res_polybb.left
+            y_res = -_res_polybb.bottom
+            res_lay = layouter.place(_res_lay, x=x_res, y=y_res)
+            res_polybb = res_lay.bounds(mask=poly.mask)
+            res_libb = res_lay.bounds(mask=li.mask)
+
+            if polyport2bb is not None:
+                assert n > 0
+                assert liport2bb is not None
+
+                net = nets[f"res2_{n - 1}"]
+                shape = _geo.Rect.from_rect(rect=polyport2bb, right=res_polybb.right)
+                layouter.add_wire(net=net, wire=poly, shape=shape)
+
+                shape = _geo.Rect.from_rect(rect=liport2bb, right=res_libb.right)
+                layouter.add_wire(net=net, wire=li, shape=shape)
+
+            if n == 0:
+                vq3res2_libb = res_lay.bounds(mask=li.mask, net=nets.vq3)
+
+            if n < (self.res2_n - 1):
+                net = nets[f"res2_{n}"]
+                polyport2bb = res_lay.bounds(mask=poly.mask, net=net)
+                liport2bb = res_lay.bounds(mask=li.mask, net=net)
+            else:
+                vrefres2_libb = res_lay.bounds(mask=li.mask, net=nets.vref)
+
+            x = res_polybb.right + poly.min_space
+        assert vq3res2_libb is not None
+        assert vrefres2_libb is not None
+
+        # Place mosfets
+        _n1_lay = layouter.inst_layout(inst=insts.n1)
+        _n1_bb = _n1_lay.bounds()
+        _p1_lay = layouter.inst_layout(inst=insts.p1)
+        _p1_bb = _p1_lay.bounds()
+
+        x = 1.5 - min(_n1_bb.left, _p1_bb.left)
+        dx_mos = _n1_bb.width + 0.5
+        y_n = 3*dy_pnp + 0.5 - _n1_bb.bottom
+        y_p = y_n + _n1_bb.top + 0.8 - _p1_bb.bottom
+
+        n1_lay = layouter.place(_n1_lay, x=x, y=y_n)
+        n1_actbb = n1_lay.bounds(mask=difftap.mask)
+        n1_nsdmbb = n1_lay.bounds(mask=nsdm.mask)
+        n1_hvibb = n1_lay.bounds(mask=hvi.mask)
+        n1_polybb = n1_lay.bounds(mask=poly.mask)
+        p1_lay = layouter.place(_p1_lay, x=x, y=y_p)
+        p1_actbb = p1_lay.bounds(mask=difftap.mask)
+        p1_psdmbb = p1_lay.bounds(mask=psdm.mask)
+        p1_hvibb = p1_lay.bounds(mask=hvi.mask)
+        p1_polybb = p1_lay.bounds(mask=poly.mask)
+
+        x += dx_mos
+        n2_lay = layouter.place(insts.n2, x=x, y=y_n)
+        n2_actbb = n2_lay.bounds(mask=difftap.mask)
+        n2_nsdmbb = n2_lay.bounds(mask=nsdm.mask)
+        n2_polybb = n2_lay.bounds(mask=poly.mask)
+        p2_lay = layouter.place(insts.p2, x=x, y=y_p)
+        p2_actbb = p2_lay.bounds(mask=difftap.mask)
+        p2_polybb = p2_lay.bounds(mask=poly.mask)
+        x += dx_mos
+        p3_lay = layouter.place(insts.p3, x=x, y=y_p)
+        p3_nwbb = p3_lay.bounds(mask=nwm.mask)
+        p3_actbb = p3_lay.bounds(mask=difftap.mask)
+        p3_psdmbb = p3_lay.bounds(mask=psdm.mask)
+        p3_polybb = p3_lay.bounds(mask=poly.mask)
+
+        # n_gate
+        net = nets.n_gate
+
+        _chngaten_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=difftap, bottom_implant=nsdm,
+            bottom_height=n1_actbb.height, bottom_enclosure="wide",
+        )
+        _chngaten_actbb = _chngaten_lay.bounds(mask=difftap.mask)
+        x = n1_polybb.right - _chngaten_actbb.left
+        y = n1_actbb.bottom - _chngaten_actbb.bottom
+        chngaten_lay = layouter.place(_chngaten_lay, x=x, y=y)
+        chngaten_libb = chngaten_lay.bounds(mask=li.mask)
+
+        _chngatep_lay = layouter.wire_layout(
+            net=net, well_net=nets.vdd, wire=licon,
+            bottom=difftap, bottom_implant=psdm, bottom_well=nwm,
+            bottom_height=p1_actbb.height, bottom_enclosure="wide",
+        )
+        _chngatep_actbb = _chngatep_lay.bounds(mask=difftap.mask)
+        x = p1_polybb.right - _chngatep_actbb.left
+        y = p1_actbb.bottom - _chngatep_actbb.bottom
+        chngatep_lay = layouter.place(_chngatep_lay, x=x, y=y)
+        chngatep_libb = chngatep_lay.bounds(mask=li.mask)
+
+        _chngatepad_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=poly, bottom_enclosure="tall",
+        )
+        _chngatepad_polybb = _chngatepad_lay.bounds(mask=poly.mask)
+        # x = x # Keep the x value
+        y = n1_polybb.top - _chngatepad_polybb.bottom
+        chngatepad_lay = layouter.place(_chngatepad_lay, x=x, y=y)
+        chngatepad_polybb = chngatepad_lay.bounds(mask=poly.mask)
+
+        shape = _geo.Rect.from_rect(
+            rect=chngaten_libb, top=chngatep_libb.top)
+        layouter.add_wire(net=net, wire=li, shape=shape)
+
+        ngatepad_bb = _geo.Rect.from_rect(
+            rect=chngatepad_polybb, left=n1_polybb.left, right=n2_polybb.right,
+        )
+        layouter.add_wire(net=net, wire=poly, shape=ngatepad_bb)
+
+        # p_gate
+        net = nets.p_gate
+
+        _chpgaten_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=difftap, bottom_implant=nsdm,
+            bottom_height=n2_actbb.height, bottom_enclosure="wide",
+        )
+        _chpgaten_actbb = _chpgaten_lay.bounds(mask=difftap.mask)
+        x = n2_polybb.right - _chpgaten_actbb.left
+        y = n2_actbb.bottom - _chpgaten_actbb.bottom
+        chpgaten_lay = layouter.place(_chpgaten_lay, x=x, y=y)
+        chpgaten_libb = chpgaten_lay.bounds(mask=li.mask)
+
+        _chpgatep_lay = layouter.wire_layout(
+            net=net, well_net=nets.vdd, wire=licon,
+            bottom=difftap, bottom_implant=psdm, bottom_well=nwm,
+            bottom_height=p1_actbb.height, bottom_enclosure="wide",
+        )
+        _chpgatep_actbb = _chpgatep_lay.bounds(mask=difftap.mask)
+        x = p2_polybb.right - _chpgatep_actbb.left
+        y = p2_actbb.bottom - _chpgatep_actbb.bottom
+        chpgatep_lay = layouter.place(_chpgatep_lay, x=x, y=y)
+        chpgatep_libb = chpgatep_lay.bounds(mask=li.mask)
+
+        _chpgatepad_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=poly, bottom_enclosure="tall",
+        )
+        _chpgatepad_polybb = _chpgatepad_lay.bounds(mask=poly.mask)
+        # x = x # Keep the x value
+        y = p2_polybb.bottom - _chngatepad_polybb.top
+        chpgatepad_lay = layouter.place(_chpgatepad_lay, x=x, y=y)
+        chpgatepad_polybb = chpgatepad_lay.bounds(mask=poly.mask)
+
+        shape = _geo.Rect.from_rect(
+            rect=chpgaten_libb, top=chpgatep_libb.top)
+        layouter.add_wire(net=net, wire=li, shape=shape)
+
+        pgatepad_bb = _geo.Rect.from_rect(
+            rect=chpgatepad_polybb, left=p1_polybb.left, right=p3_polybb.right,
+        )
+        layouter.add_wire(net=net, wire=poly, shape=pgatepad_bb)
+
+        # vq1
+        net = nets.vq1
+
+        lay = lay_matrix[1][1] # pnp1
+        m1pinbb = lay.bounds(mask=m1pin.mask, net=nets.vq1)
+        via1_lay = layouter.add_wire(net=nets.vq1, wire=via1, bottom_shape=m1pinbb)
+        via1vq1pnp1_m2bb = via1_lay.bounds(mask=m2.mask)
+
+        _chvq1n1_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=difftap, bottom_implant=nsdm,
+            bottom_height=n1_actbb.height, bottom_enclosure="wide",
+        )
+        _chvq1n1_actbb = _chvq1n1_lay.bounds(mask=difftap.mask)
+        _mconvq1m1_lay = layouter.wire_layout(
+            net=net, wire=mcon, bottom_height=n1_actbb.height,
+            top_height=n1_actbb.height,
+        )
+        _via1vq1m1_lay = layouter.wire_layout(
+            net=net, wire=via1, bottom_height=n1_actbb.height,
+            top_height=n1_actbb.height,
+        )
+
+        x = n1_polybb.left - _chvq1n1_actbb.right
+        y = n1_actbb.bottom - _chvq1n1_actbb.bottom
+        layouter.place(_chvq1n1_lay, x=x, y=y)
+        layouter.place(_mconvq1m1_lay, x=x, y=y)
+        via1vq1n1_lay = layouter.place(_via1vq1m1_lay, x=x, y=y)
+        via1vq1n1_m2bb = via1vq1n1_lay.bounds(mask=m2.mask)
+
+        shape = _geo.Rect.from_rect(
+            rect=via1vq1n1_m2bb, right=via1vq1pnp1_m2bb.right
+        )
+        layouter.add_wire(net=net, wire=m2, shape=shape)
+        shape = _geo.Rect.from_rect(
+            rect=via1vq1pnp1_m2bb, top=via1vq1n1_m2bb.top
+        )
+        layouter.add_wire(net=net, wire=m2, shape=shape)
+
+        # vq2
+        net = nets.vq2
+        lay0 = lay_matrix[0][0]
+        lay0_m1pinbb = lay0.bounds(mask=m1pin.mask, net=net)
+        lay1 = lay_matrix[0][1]
+        lay1_m1pinbb = lay1.bounds(mask=m1pin.mask, net=net)
+        lay2 = lay_matrix[2][1]
+        lay2_m1pinbb = lay2.bounds(mask=m1pin.mask, net=net)
+        lay3 = lay_matrix[2][2]
+        lay3_m1pinbb = lay3.bounds(mask=m1pin.mask, net=net)
+
+        shape1 = _geo.Rect.from_rect(rect=lay0_m1pinbb, right=lay1_m1pinbb.right)
+        layouter.add_wire(net=net, wire=m1, shape=shape1)
+        shape2 = _geo.Rect.from_rect(rect=lay2_m1pinbb, right=lay3_m1pinbb.right)
+        layouter.add_wire(net=net, wire=m1, shape=shape2)
+
+        o = res1vq2_libb.center
+        mconvq2res1_lay = layouter.add_wire(net=net, wire=mcon, columns=2, origin=o)
+        mconvq2res1_m1bb = mconvq2res1_lay.bounds(mask=m1.mask)
+
+        w = mconvq2res1_m1bb.width
+        left = mconvq2res1_m1bb.left
+        right = 2*dx_pnp + 0.5*w
+        top = shape1.top
+        bottom = top - w
+        shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+
+        # right = right; keep same right
+        left = right - w
+        # bottom = bottom; keep same bottom
+        top = shape2.top
+        shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+
+        shape = _geo.Rect.from_rect(rect=mconvq2res1_m1bb, bottom=bottom)
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+
+        # vq2r1
+        _mconvq2r1res1_lay = layouter.wire_layout(net=net, wire=mcon, rows=2)
+        _mconvq2r1res1_libb = _mconvq2r1res1_lay.bounds(mask=li.mask)
+
+        x = res1vq2r1_libb.center.x
+        y = res1vq2r1_libb.bottom - _mconvq2r1res1_libb.bottom
+        mconvq2r1res1_lay = layouter.place(_mconvq2r1res1_lay, x=x, y=y)
+        mconvq2r1res1_m1bb = mconvq2r1res1_lay.bounds(mask=m1.mask)
+
+        _chvq2r1n2_lay = layouter.wire_layout(
+            net=net, wire=licon, bottom=difftap, bottom_implant=nsdm,
+            bottom_height=n2_actbb.height, bottom_enclosure="wide",
+            top_height=n2_actbb.height,
+        )
+        _chvq2r1n2_actbb = _chvq2r1n2_lay.bounds(mask=difftap.mask)
+        _mconvq2r1n2_lay = layouter.wire_layout(
+            net=net, wire=mcon,
+            bottom_height=n2_actbb.height, top_height=n2_actbb.height,
+        )
+
+        x = n2_polybb.left - _chvq2r1n2_actbb.right
+        y = n2_actbb.bottom - _chvq2r1n2_actbb.bottom
+        layouter.place(_chvq2r1n2_lay, x=x, y=y)
+        mconvq2r1n2_lay = layouter.place(_mconvq2r1n2_lay, x=x, y=y)
+        mconvq2r1n2_m1bb = mconvq2r1n2_lay.bounds(mask=m1.mask)
+
+        shape = _geo.Rect.from_rect(rect=mconvq2r1res1_m1bb, right=mconvq2r1n2_m1bb.right)
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+        shape = _geo.Rect.from_rect(rect=mconvq2r1n2_m1bb, bottom=mconvq2r1res1_m1bb.bottom)
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+
+        # vq3
+        net = nets.vq3
+
+        lay0 = lay_matrix[0][2]
+        lay0_m1pinbb = lay0.bounds(mask=m1pin.mask, net=net)
+        lay1 = lay_matrix[1][2]
+        lay1_m1pinbb = lay1.bounds(mask=m1pin.mask, net=net)
+        lay2 = lay_matrix[1][0]
+        lay2_m1pinbb = lay2.bounds(mask=m1pin.mask, net=net)
+        lay3 = lay_matrix[2][0]
+        lay3_m1pinbb = lay3.bounds(mask=m1pin.mask, net=net)
+
+        shape1 = _geo.Rect.from_rect(rect=lay0_m1pinbb, top=lay1_m1pinbb.top)
+        layouter.add_wire(net=net, wire=m1, shape=shape1)
+        shape2 = _geo.Rect.from_rect(rect=lay3_m1pinbb, bottom=dy_pnp)
+        layouter.add_wire(net=net, wire=m1, shape=shape2)
+
+        via1vq3_lay1 = layouter.add_wire(
+            net=net, wire=via1, x=shape2.center.x, y=dy_pnp,
+            bottom_width=shape2.width,
+        )
+        via1vq3_m2bb1 = via1vq3_lay1.bounds(mask=m2.mask)
+        layouter.add_wire(
+            net=net, wire=via1, x=shape1.center.x, y=dy_pnp,
+            bottom_width=shape1.width,
+        )
+
+        _mconvq3res2_lay = layouter.wire_layout(net=net, wire=mcon, rows=2)
+        _mconvq3res2_libb = _mconvq3res2_lay.bounds(mask=li.mask)
+        x = vq3res2_libb.center.x
+        y = vq3res2_libb.bottom - _mconvq3res2_libb.bottom
+        mconvq3res2_lay = layouter.place(_mconvq3res2_lay, x=x, y=y)
+        mconvq3res2_m1bb = mconvq3res2_lay.bounds(mask=m1.mask)
+        via1vq3res2_lay = layouter.add_wire(
+            net=net, wire=via1, rows=2, x=x, y=dy_pnp,
+        )
+        via1vq3res2_m1bb = via1vq3res2_lay.bounds(mask=m1.mask)
+        via1vq3res2_m2bb = via1vq3res2_lay.bounds(mask=m2.mask)
+
+        shape = _geo.Rect.from_rect(
+            rect=via1vq3res2_m1bb, bottom=mconvq3res2_m1bb.bottom,
+        )
+        layouter.add_wire(net=net, wire=m1, shape=shape)
+
+        shape = _geo.Rect.from_rect(
+            rect=via1vq3res2_m2bb, left=via1vq3_m2bb1.left,
+        )
+        layouter.add_wire(net=net, wire=m2, shape=shape)
+
+        # vref
+        net = nets.vref
+
+        _chvrefp_lay = layouter.wire_layout(
+            net=net, well_net=nets.vdd, wire=licon,
+            bottom=difftap, bottom_implant=psdm, bottom_well=nwm,
+            bottom_height=p1_actbb.height, bottom_enclosure="wide",
+        )
+        _chvrefp_actbb = _chvrefp_lay.bounds(mask=difftap.mask)
+        x = p3_polybb.right - _chvrefp_actbb.left
+        y = p3_actbb.bottom - _chvrefp_actbb.bottom
+        chvrefp_lay = layouter.place(_chvrefp_lay, x=x, y=y)
+        chvrefp_libb = chvrefp_lay.bounds(mask=li.mask)
+
+        shape = _geo.Rect.from_rect(rect=vrefres2_libb, top=chvrefp_libb.top)
+        layouter.add_wire(net=net, wire=li, shape=shape)
+        shape = _geo.Rect.from_rect(rect=chvrefp_libb, right=vrefres2_libb.right)
+        layouter.add_wire(net=net, wire=li, pin=lipin, shape=shape)
+
+        # vdd
+        net = nets.vdd
+
+        _chvdd_lay = layouter.wire_layout(
+            net=net, well_net=net, wire=licon,
+            bottom=difftap, bottom_implant=psdm, bottom_well=nwm,
+            bottom_height=p1_actbb.height, bottom_enclosure="wide",
+        )
+        _chvdd_actbb = _chvdd_lay.bounds(mask=difftap.mask)
+        _mconvdd_lay = layouter.wire_layout(
+            net=net, wire=mcon, bottom_height=p1_actbb.height,
+        )
+
+        x = p1_polybb.left - _chvdd_actbb.right
+        y = p1_actbb.bottom - _chvdd_actbb.bottom
+        lay = layouter.place(_chvdd_lay, x=x, y=y)
+        actbb = lay.bounds(mask=difftap.mask)
+        libb = lay.bounds(mask=li.mask)
+        lay = layouter.place(_mconvdd_lay, x=x, y=y)
+        mconp1_m1bb = lay.bounds(mask=m1.mask)
+        x = p2_polybb.left - _chvdd_actbb.right
+        layouter.place(_chvdd_lay, x=x, y=y)
+        layouter.place(_mconvdd_lay, x=x, y=y)
+        x = p3_polybb.left - _chvdd_actbb.right
+        layouter.place(_chvdd_lay, x=x, y=y)
+        lay = layouter.place(_mconvdd_lay, x=x, y=y)
+        mconp3_m1bb = lay.bounds(mask=m1.mask)
+
+        _chvddnw_lay = layouter.wire_layout(
+            net=net, well_net=net, wire=licon, columns=2,
+            bottom=difftap, bottom_implant=nsdm, bottom_well=nwm,
+            bottom_height=p1_actbb.height, bottom_enclosure="wide",
+        )
+        _chvddnw_actbb = _chvddnw_lay.bounds(mask=difftap.mask)
+
+        x = actbb.left - 0.5 - _chvddnw_actbb.right
+        chvddnw_lay = layouter.place(_chvddnw_lay, x=x, y=y)
+        chvddnw_nwbb = chvddnw_lay.bounds(mask=nwm.mask)
+        chvddnw_libb = chvddnw_lay.bounds(mask=li.mask)
+
+        shape = _geo.Rect.from_rect(rect=libb, left=chvddnw_libb.left)
+        layouter.add_wire(net=net, wire=li, shape=shape)
+
+        shape = _geo.Rect.from_rect(rect=mconp1_m1bb, right=mconp3_m1bb.right)
+        layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
+
+        # nsdm; cover poly contact hole
+        shape = _geo.Rect.from_rect(
+            rect=n1_nsdmbb, right=n2_nsdmbb.right, top=ngatepad_bb.top,
+        )
+        layouter.add_portless(prim=nsdm, shape=shape)
+
+        # psdm; cover poly contact hole
+        shape = _geo.Rect.from_rect(
+            rect=p1_psdmbb, right=p3_psdmbb.right, bottom=pgatepad_bb.bottom,
+        )
+        layouter.add_portless(prim=psdm, shape=shape)
+
+        # nwell
+        shape = _geo.Rect.from_rect(rect=p3_nwbb, left=chvddnw_nwbb.left)
+        layouter.add_wire(net=nets.vdd, wire=nwm, shape=shape)
+
+        # hvi
+        shape = _geo.Rect(
+            left=0.0, bottom=n1_hvibb.bottom, right=3*dx_pnp, top=p1_hvibb.top,
+        )
+        layouter.add_portless(prim=hvi, shape=shape)
+
+        # Boundary
+        shape = _geo.Rect.from_rect(rect=layout.bounds(), bias=0.2)
+        layout.boundary = shape
+        layouter.add_portless(prim=bnd, shape=shape)
+
+
+class ConnectedBandGap(_lbry._Cell[_lbry.Library]):
+    def __init__(self, *, lib: _lbry.Library):
+        super().__init__(lib=lib, name="ConnectedBandGap")
+
+        ckt = self.new_circuit()
+        layouter = self.new_circuitlayouter()
+        layout = layouter.layout
+
+        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]
+        via1 = cast(_prm.Via, _prims.via)
+        m2 = cast(_prm.MetalWire, _prims.m2)
+        via2 = cast(_prm.Via, _prims.via2)
+        m3 = cast(_prm.MetalWire, _prims.m3)
+        assert m3.pin is not None
+        m3pin = m3.pin[0]
+        bnd = cast(_prm.Auxiliary, _prims.prBoundary)
+
+        bg_cell = _BandGap3V3(lib=lib)
+        lib.cells += bg_cell
+        _bg_bb = bg_cell.layout.boundary
+        assert _bg_bb is not None
+        _bgvref = bg_cell.circuit.nets.vref
+        _bgvref_lipinbb = bg_cell.layout.bounds(mask=lipin.mask, net=_bgvref)
+
+        bg = ckt.instantiate(bg_cell, name="bg")
+
+        # Add pins + nets for the io signals of the bandgap
+        vss_pinname = "io_analog[7]"
+        vss_m3pinbb = _frm.toppins[vss_pinname]
+        vss = ckt.new_net(name=vss_pinname, external=True, childports=bg.ports.vss)
+        layouter.add_wire(net=vss, wire=m3, pin=m3pin, shape=vss_m3pinbb)
+
+        vdd_pinname = "io_analog[9]"
+        vdd_m3pinbb = _frm.toppins[vdd_pinname]
+        vdd = ckt.new_net(name=vdd_pinname, external=True, childports=bg.ports.vdd)
+        layouter.add_wire(net=vdd, wire=m3, pin=m3pin, shape=vdd_m3pinbb)
+
+        vref_pinname = "io_analog[8]"
+        vref_m3pinbb = _frm.toppins[vref_pinname]
+        vref = ckt.new_net(name=vref_pinname, external=True, childports=bg.ports.vref)
+        layouter.add_wire(net=vref, wire=m3, pin=m3pin, shape=vref_m3pinbb)
+
+        # Place bandgap, align vref pin with top pin
+        x = vref_m3pinbb.center.x - _bgvref_lipinbb.center.x
+        y = vref_m3pinbb.bottom - 5.0 - _bg_bb.top
+        bg_lay = layouter.place(bg, x=x, y=y)
+        bgvss_lipinbb = bg_lay.bounds(mask=lipin.mask, net=vss, depth=1)
+        bgvdd_m1pinbb = bg_lay.bounds(mask=m1pin.mask, net=vdd, depth=1)
+        bgvref_lipinbb = bg_lay.bounds(mask=lipin.mask, net=vref, depth=1)
+
+        # Boundary
+        layout.boundary = _frm.boundary
+        layouter.add_portless(prim=bnd, shape=_frm.boundary)
+
+        # vss
+        net = vss
+
+        layouter.add_wire(
+            net=net, wire=mcon, bottom_shape=bgvss_lipinbb, top_shape=bgvss_lipinbb,
+        )
+        layouter.add_wire(
+            net=net, wire=via1, bottom_shape=bgvss_lipinbb, top_shape=bgvss_lipinbb,
+        )
+        lay = layouter.add_wire(
+            net=net, wire=via2, bottom_shape=bgvss_lipinbb, top_shape=bgvss_lipinbb,
+        )
+        m3bb = lay.bounds(mask=m3.mask)
+
+        shape = _geo.Rect.from_rect(rect=m3bb, right=vss_m3pinbb.right)
+        layouter.add_wire(net=net, wire=m3, shape=shape)
+        shape = _geo.Rect.from_rect(rect=vss_m3pinbb, bottom=m3bb.bottom)
+        layouter.add_wire(net=net, wire=m3, shape=shape)
+
+        # vss
+        net = vdd
+
+        layouter.add_wire(
+            net=net, wire=via1, bottom_shape=bgvdd_m1pinbb, top_shape=bgvdd_m1pinbb,
+        )
+        lay = layouter.add_wire(
+            net=net, wire=via2, bottom_shape=bgvdd_m1pinbb, top_shape=bgvdd_m1pinbb,
+        )
+        m3bb = lay.bounds(mask=m3.mask)
+
+        shape = _geo.Rect.from_rect(rect=m3bb, left=vdd_m3pinbb.left)
+        layouter.add_wire(net=net, wire=m3, shape=shape)
+        shape = _geo.Rect.from_rect(rect=vdd_m3pinbb, bottom=m3bb.bottom)
+        layouter.add_wire(net=net, wire=m3, shape=shape)
+
+        # vref
+        net = vref
+
+        layouter.add_wire(
+            net=net, wire=mcon, bottom_shape=bgvref_lipinbb, top_shape=bgvref_lipinbb,
+        )
+        layouter.add_wire(
+            net=net, wire=via1, bottom_shape=bgvref_lipinbb, top_shape=bgvref_lipinbb,
+        )
+        lay = layouter.add_wire(
+            net=net, wire=via2, bottom_shape=bgvref_lipinbb, top_shape=bgvref_lipinbb,
+        )
+        m3bb = lay.bounds(mask=m3.mask)
+
+        shape = _geo.Rect.from_rect(rect=m3bb, top=vref_m3pinbb.bottom)
+        layouter.add_wire(net=net, wire=m3, shape=shape)
diff --git a/doitcode/generate.py b/doitcode/generate.py
index 4c677a4..8297498 100644
--- a/doitcode/generate.py
+++ b/doitcode/generate.py
@@ -14,7 +14,7 @@
 
 from c4m.pdk import sky130
 
-from . import frame as _frm, sram as _ram
+from . import frame as _frm, sram as _ram, bandgap as _bg
 
 
 __all__ = ["gen_gds"]
@@ -65,11 +65,18 @@
     ckt = top.new_circuit()
     layouter = top.new_circuitlayouter()
 
+    # Add the BandGap
+    bandgap_cell = _bg.ConnectedBandGap(lib=lib)
+    bandgap_inst = ckt.instantiate(bandgap_cell, name="bandgaptop")
+    layouter.place(bandgap_inst, x=0.0, y=0.0)
+
     # Add the SRAM
     sram_cell = _ram.ConnectedSRAM(lib=lib)
     sram_inst = ckt.instantiate(sram_cell, name="sramtop")
     layouter.place(sram_inst, origin=_geo.origin)
 
+    lib.cells += (bandgap_cell, sram_cell)
+
     # Add circuit and
     layouter.layout.boundary = _frm.boundary
 
diff --git a/gds/user_analog_project_wrapper.gds.gz b/gds/user_analog_project_wrapper.gds.gz
index 1bfbf8c..ca5b026 100644
--- a/gds/user_analog_project_wrapper.gds.gz
+++ b/gds/user_analog_project_wrapper.gds.gz
Binary files differ
diff --git a/netgen/user_analog_project_wrapper.spice b/netgen/user_analog_project_wrapper.spice
index 6b4e7ab..6776910 100644
--- a/netgen/user_analog_project_wrapper.spice
+++ b/netgen/user_analog_project_wrapper.spice
@@ -4,6 +4,10 @@
 
 .ends
 
+.subckt ConnectedBandGap
+
+.ends
+
 .subckt user_analog_project_wrapper_empty gpio_analog[0] gpio_analog[10] gpio_analog[11]
 + gpio_analog[12] gpio_analog[13] gpio_analog[14] gpio_analog[15] gpio_analog[16]
 + gpio_analog[17] gpio_analog[1] gpio_analog[2] gpio_analog[3] gpio_analog[4] gpio_analog[5]
@@ -227,6 +231,8 @@
 + io_analog[4] io_analog[5]
 + ConnectedSRAM
 
+Xbg io_analog[7] io_analog[9] io_analog[8] ConnectedBandGap
+
 Xempty gpio_analog[0] gpio_analog[10] gpio_analog[11]
 + gpio_analog[12] gpio_analog[13] gpio_analog[14] gpio_analog[15] gpio_analog[16]
 + gpio_analog[17] gpio_analog[1] gpio_analog[2] gpio_analog[3] gpio_analog[4] gpio_analog[5]
diff --git a/verilog/rtl/blocks.v b/verilog/rtl/blocks.v
index c4db2d6..f8a982d 100644
--- a/verilog/rtl/blocks.v
+++ b/verilog/rtl/blocks.v
@@ -8,6 +8,14 @@
 
 endmodule
 
+module ConnectedBandGap (
+    inout vss,
+    inout vdd,
+    out vref
+);
+
+endmodule
+
 
 module user_analog_project_wrapper_empty (
     inout vdda1,
diff --git a/verilog/rtl/user_analog_project_wrapper.v b/verilog/rtl/user_analog_project_wrapper.v
index 95fc0c5..8798597 100644
--- a/verilog/rtl/user_analog_project_wrapper.v
+++ b/verilog/rtl/user_analog_project_wrapper.v
@@ -91,4 +91,9 @@
     .vdd(io_analog[5])
 )
 
+ConnectedBandGap bg (
+    .vss(io_analog[7]),
+    .vdd(io_analog[9])
+)
+
 endmodule	// user_analog_project_wrapper