First connected out version * Add logic inside IORO signal to enable and drive output of RO. * Connected input/outputs of instantiated subcells to pins on the top cell.
diff --git a/doitcode/frame.py b/doitcode/frame.py index fa2256e..885c797 100644 --- a/doitcode/frame.py +++ b/doitcode/frame.py
@@ -2,21 +2,45 @@ from pdkmaster.technology import geometry as _geo -__all__ = ["boundary", "ioanalog"] +__all__ = ["boundary", "toppins"] # The metal3 pins for the IOs boundary = _geo.Rect(left=0.0, bottom=0.0, right=2920.0, top=3520.0) -ioanalog = ( - _geo.Rect(left=2911.50, bottom=3389.92, right=2924.00, top=3414.92), # 0 - _geo.Rect(left=2832.97, bottom=3511.50, right=2857.97, top=3524.00), # 1 - _geo.Rect(left=2326.97, bottom=3511.50, right=2351.97, top=3524.00), # 2 - _geo.Rect(left=2066.97, bottom=3511.50, right=2091.97, top=3524.00), # 3 - _geo.Rect(left=1594.97, bottom=3511.50, right=1619.97, top=3524.00), # 4 - _geo.Rect(left=1086.47, bottom=3511.50, right=1111.47, top=3524.00), # 5 - _geo.Rect(left=827.97, bottom=3511.50, right=852.97, top=3524.00), # 6 - _geo.Rect(left=600.97, bottom=3511.50, right=625.97, top=3524.00), # 7 - _geo.Rect(left=340.97, bottom=3511.50, right=365.97, top=3524.00), # 8 - _geo.Rect(left=80.97, bottom=3511.50, right=105.97, top=3524.00), # 9 - _geo.Rect(left=-4.00, bottom=3401.21, right=8.50, top=3426.21), # 10 -) \ No newline at end of file +toppins = { + "io_analog[0]": _geo.Rect(left=2911.50, bottom=3389.92, right=2924.00, top=3414.92), + "io_analog[1]": _geo.Rect(left=2832.97, bottom=3511.50, right=2857.97, top=3524.00), + "io_analog[2]": _geo.Rect(left=2326.97, bottom=3511.50, right=2351.97, top=3524.00), + "io_analog[3]": _geo.Rect(left=2066.97, bottom=3511.50, right=2091.97, top=3524.00), + "io_analog[4]": _geo.Rect(left=1594.97, bottom=3511.50, right=1619.97, top=3524.00), + "io_analog[5]": _geo.Rect(left=1086.47, bottom=3511.50, right=1111.47, top=3524.00), + "io_analog[6]": _geo.Rect(left=827.97, bottom=3511.50, right=852.97, top=3524.00), + "io_analog[7]": _geo.Rect(left=600.97, bottom=3511.50, right=625.97, top=3524.00), + "io_analog[8]": _geo.Rect(left=340.97, bottom=3511.50, right=365.97, top=3524.00), + "io_analog[9]": _geo.Rect(left=80.97, bottom=3511.50, right=105.97, top=3524.00), + "io_analog[10]": _geo.Rect(left=-4.00, bottom=3401.21, right=8.50, top=3426.21), + "io_oeb[13]": _geo.Rect(left=2917.60, bottom=2947.36, right=2924.00, top=2947.92), + "io_out[13]": _geo.Rect(left=2917.60, bottom=2941.45, right=2924.00, top=2942.01), + "io_in[13]": _geo.Rect(left=2917.60, bottom=2935.54, right=2924.00, top=2936.10), + "io_in_3v3[13]": _geo.Rect(left=2917.60, bottom=2929.63, right=2924.00, top=2930.19), + "gpio_noesd[6]": _geo.Rect(left=2917.60, bottom=2923.72, right=2924.00, top=2924.28), + "gpio_analog[6]": _geo.Rect(left=2917.60, bottom=2917.81, right=2924.00, top=2918.37), + "io_oeb[12]": _geo.Rect(left=2917.60, bottom=2500.25, right=2924.00, top=2500.81), + "io_out[12]": _geo.Rect(left=2917.60, bottom=2494.34, right=2924.00, top=2494.90), + "io_in[12]": _geo.Rect(left=2917.60, bottom=2488.43, right=2924.00, top=2488.99), + "io_in_3v3[12]": _geo.Rect(left=2917.60, bottom=2482.52, right=2924.00, top=2483.08), + "gpio_noesd[5]": _geo.Rect(left=2917.60, bottom=2476.61, right=2924.00, top=2477.17), + "gpio_analog[5]": _geo.Rect(left=2917.60, bottom=2470.70, right=2924.00, top=2471.26), + "io_oeb[11]": _geo.Rect(left=2917.60, bottom=2278.14, right=2924.00, top=2278.70), + "io_out[11]": _geo.Rect(left=2917.60, bottom=2272.23, right=2924.00, top=2272.79), + "io_in[11]": _geo.Rect(left=2917.60, bottom=2266.32, right=2924.00, top=2266.88), + "io_in_3v3[11]": _geo.Rect(left=2917.60, bottom=2260.41, right=2924.00, top=2260.97), + "gpio_noesd[4]": _geo.Rect(left=2917.60, bottom=2254.50, right=2924.00, top=2255.06), + "gpio_analog[4]": _geo.Rect(left=2917.60, bottom=2248.59, right=2924.00, top=2249.15), + "io_oeb[14]": _geo.Rect(left=-4.00, bottom=2528.10, right=2.40, top=2528.66), + "io_out[14]": _geo.Rect(left=-4.00, bottom=2534.01, right=2.40, top=2534.57), + "io_in[14]": _geo.Rect(left=-4.00, bottom=2539.92, right=2.40, top=2540.48), + "io_in_3v3[14]": _geo.Rect(left=-4.00, bottom=2545.83, right=2.40, top=2546.39), + "gpio_noesd[7]": _geo.Rect(left=-4.00, bottom=2551.74, right=2.40, top=2552.30), + "gpio_analog[7]": _geo.Rect(left=-4.00, bottom=2557.65, right=2.40, top=2558.21), +}
diff --git a/doitcode/generate.py b/doitcode/generate.py index a5b823c..04aec13 100644 --- a/doitcode/generate.py +++ b/doitcode/generate.py
@@ -2,50 +2,184 @@ # PDKMaster & co. don't put a license requirement on the generated files. # The generated gds files in this project are released under the LGPL 2.0 or later license. from pathlib import Path -from typing import Any, cast +from typing import Dict, Any, cast import pya as _pya # Disable type checking pya = cast(Any, _pya) -from pdkmaster.technology import (geometry as _geo, primitive as _prm) -from pdkmaster.design import library as _lib +from pdkmaster.technology import geometry as _geo, primitive as _prm +from pdkmaster.design import layout as _lay, library as _lib from pdkmaster.io.klayout import export as _klexp from c4m.pdk import sky130 from . import frame as _frm -def gen_gds(*, gds_out: Path, gds_empty: Path): - top_name = "user_analog_project_wrapper" - prims = sky130.tech.primitives __all__ = ["gen_gds"] - lib = _lib.Library( - name="MPW4IOTest", tech=sky130.tech, cktfab=sky130.cktfab, layoutfab=sky130.layoutfab, - ) +# Match nets in IOConnected to pins +_netlookup = { + "vss": "io_analog[0]", + "vdd": "io_analog[1]", + "ioout_pad": "io_analog[2]", + "ioinout_pad": "io_analog[3]", + "d_core": "io_analog[4]", + "de_core": "io_analog[5]", + "ioinout_core": "io_analog[6]", + "ioin_core": "io_analog[7]", + "ioin_pad": "io_analog[8]", + "iovdd": "io_analog[9]", + "iovss": "io_analog[10]", + "ro_de": "gpio_analog[4]", + "ro_en": "gpio_analog[5]", + "ro_out": "gpio_noesd[7]", +} + +# Dimensions for top level interconnects +_conn_width = 25.0 +_conn_space = 2.0 +_conn_pitch = _conn_width + _conn_space def _gen_ioblock(*, lib: _lib.Library): + tech = lib.tech + prims = tech.primitives + + 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] + m5 = cast(_prm.MetalWire, prims.m5) + assert m5.pin is not None + m5pin = m5.pin[0] + # Create the IOBlock ioblock = lib.new_cell(name="IOBlock") ckt = ioblock.new_circuit() + nets = ckt.nets layouter = ioblock.new_circuitlayouter() + for net_name in _netlookup.keys(): + if net_name.startswith("ro_"): + continue + ckt.new_net(name=net_name, external=True) + x = 0.0 lay = None - for cell_name in ( - "IOPadIOVss", "IOPadIOVdd", "IOPadIn", "IOPadInOut", "IOPadOut", - "IOPadVdd", "IOPadVss", + d_m1pin_bounds1 = d_m1pin_bounds2 = None + de_m1pin_bounds1 = de_m1pin_bounds2 = None + for (cell_name, top_pad_netname, cell_pad_netname) in ( + ("IOPadIOVss", "iovss", "iovss"), + ("IOPadIOVdd", "iovdd", "iovdd"), + ("IOPadIn", "ioin_pad", "pad"), + ("IOPadInOut", "ioinout_pad", "pad"), + ("IOPadOut", "ioout_pad", "pad"), + ("IOPadVdd", "vdd", "vdd"), + ("IOPadVss", "vss", "vss"), ): cell = sky130.iolib.cells[cell_name] inst = ckt.new_instance(name=cell_name, object_=cell) lay = layouter.place(inst, x=x, y=0.0) + + nets[top_pad_netname].childports += inst.ports[cell_pad_netname] + if cell_name == "IOPadIn": + nets.ioin_core.childports += inst.ports.s + elif cell_name == "IOPadInOut": + nets.ioinout_core.childports += inst.ports.s + if cell_name in ("IOPadInOut", "IOPadOut"): + nets.d_core.childports += inst.ports.d + nets.de_core.childports += inst.ports.de + m5pin_bounds = lay.bounds(mask=m5pin.mask) + layouter.add_wire(net=nets[top_pad_netname], wire=m5, pin=m5pin, shape=m5pin_bounds) + + if cell_name == "IOPadIn": + net = nets.ioin_core + m1pin_bounds = lay.bounds(mask=m1pin.mask, net=net, depth=1) + layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=m1pin_bounds) + elif cell_name == "IOPadInOut": + net = nets.ioinout_core + m1pin_bounds = lay.bounds(mask=m1pin.mask, net=net, depth=1) + layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=m1pin_bounds) + + d_m1pin_bounds1 = lay.bounds(mask=m1pin.mask, net=nets.d_core, depth=1) + de_m1pin_bounds1 = lay.bounds(mask=m1pin.mask, net=nets.de_core, depth=1) + elif cell_name == "IOPadOut": + d_m1pin_bounds2 = lay.bounds(mask=m1pin.mask, net=nets.d_core, depth=1) + de_m1pin_bounds2 = lay.bounds(mask=m1pin.mask, net=nets.de_core, depth=1) + assert lay.boundary is not None x = lay.boundary.right assert lay is not None + assert d_m1pin_bounds1 is not None + assert d_m1pin_bounds2 is not None + assert de_m1pin_bounds1 is not None + assert de_m1pin_bounds2 is not None + + # Connect d and de + m2_width = tech.computed.min_width(m2, down=True, up=True, min_enclosure=True) + + net = nets.d_core + bottom = max(d_m1pin_bounds1.top, d_m1pin_bounds2.top) + m1.min_space + width = d_m1pin_bounds2.right - d_m1pin_bounds1.left + _l_via = layouter.wire_layout( + net=net, wire=via, + bottom_width=width, bottom_enclosure="wide", + top_width=width, top_height=m2_width, top_enclosure="wide", + ) + _m1_bounds = _l_via.bounds(mask=m1.mask) + x = 0.5*(d_m1pin_bounds2.right + d_m1pin_bounds1.left) + y = bottom - _m1_bounds.bottom + l_via = layouter.place(_l_via, x=x, y=y) + m1_bounds = l_via.bounds(mask=m1.mask) + m2_bounds = l_via.bounds(mask=m2.mask) + layouter.add_wire( + net=net, wire=m1, shape=_geo.Rect.from_rect(rect=d_m1pin_bounds1, top=m1_bounds.top), + ) + layouter.add_wire( + net=net, wire=m1, shape=_geo.Rect.from_rect(rect=d_m1pin_bounds2, top=m1_bounds.top), + ) + _l_m2 = layouter.wire_layout( + net=net, wire=m2, pin=m2pin, width=m2_bounds.width, height=m2_width, + ) + _m2_bounds = _l_m2.bounds() + x = m2_bounds.left - _m2_bounds.left + y = m2_bounds.bottom - _m2_bounds.bottom + l_m2 = layouter.place(_l_m2, x=x, y=y) + dm2_bounds = l_m2.bounds() + + net = nets.de_core + bottom = dm2_bounds.top + m2.min_space + width = de_m1pin_bounds2.right - de_m1pin_bounds1.left + _l_via = layouter.wire_layout( + net=net, wire=via, + bottom_width=width, bottom_enclosure="wide", + top_width=width, top_height=m2_width, top_enclosure="wide", + ) + _m1_bounds = _l_via.bounds(mask=m1.mask) + x = 0.5*(de_m1pin_bounds2.right + de_m1pin_bounds1.left) + y = bottom - _m1_bounds.bottom + l_via = layouter.place(_l_via, x=x, y=y) + m1_bounds = l_via.bounds(mask=m1.mask) + m2_bounds = l_via.bounds(mask=m2.mask) + layouter.add_wire( + net=net, wire=m1, shape=_geo.Rect.from_rect(rect=de_m1pin_bounds1, top=m1_bounds.top), + ) + layouter.add_wire( + net=net, wire=m1, shape=_geo.Rect.from_rect(rect=de_m1pin_bounds2, top=m1_bounds.top), + ) + _l_m2 = layouter.wire_layout( + net=net, wire=m2, pin=m2pin, width=m2_bounds.width, height=m2_width, + ) + _m2_bounds = _l_m2.bounds() + x = m2_bounds.left - _m2_bounds.left + y = m2_bounds.bottom - _m2_bounds.bottom + layouter.place(_l_m2, x=x, y=y) assert isinstance(lay.boundary, _geo.Rect) layouter.layout.boundary = _geo.Rect.from_rect(rect=lay.boundary, left=0.0) @@ -54,44 +188,409 @@ def _gen_ioro(*, lib: _lib.Library): + tech = lib.tech + prims = tech.primitives + + li = cast(_prm.MetalWire, prims.li) + assert (li.pin is not None) and (len(li.pin) == 1) + lipin = li.pin[0] + mcon = cast(_prm.Via, prims.mcon) + m1 = cast(_prm.MetalWire, prims.m1) + assert (m1.pin is not None) and (len(m1.pin) == 1) + m1pin = m1.pin[0] + via = cast(_prm.Via, prims.via) + m2 = cast(_prm.MetalWire, prims.m2) + assert (m2.pin is not None) and (len(m2.pin) == 1) + m2pin = m2.pin[0] + via2 = cast(_prm.Via, prims.via2) + via3 = cast(_prm.Via, prims.via3) + m4 = cast(_prm.MetalWire, prims.m4) + assert (m4.pin is not None) and (len(m4.pin) == 1) + m4pin = m4.pin[0] + via4 = cast(_prm.Via, prims.via4) + m5 = cast(_prm.MetalWire, prims.m5) + assert (m5.pin is not None) and (len(m5.pin) == 1) + m5pin = m5.pin[0] + # Create the IORO ioro = lib.new_cell(name="IORO") ckt = ioro.new_circuit() + nets = ckt.nets layouter = ioro.new_circuitlayouter() + stages = 5 + + # External nets + for net_name in ("iovdd", "iovss", "vdd", "vss", "ro_de", "ro_en", "ro_out"): + ckt.new_net(name=net_name, external=True) + # Internal nets + for i in range(stages): + ckt.new_net(f"stage{i}_3v3", external=False) + ckt.new_net(f"stage{i}_1v8", external=False) + # Extra net for last d that will come from a NAND gate + ckt.new_net(f"stage{stages}_1v8", external=False) + + # Instantiate all cells and compute cell width x = 0.0 lay = None + layouts: Dict[str, _lay._Layout] = {} # First DC cells - for cell_name in ("IOPadVdd", "IOPadVss", "IOPadIOVdd", "IOPadIOVss"): + for cell_name, pad_net_name in ( + ("IOPadVdd", "vdd"), + ("IOPadVss", "vss"), + ("IOPadIOVdd", "iovdd"), + ("IOPadIOVss", "iovss"), + ): + inst_name = f"{cell_name}0" cell = sky130.iolib.cells[cell_name] - inst = ckt.new_instance(name=f"{cell_name}0", object_=cell) - lay = layouter.place(object_=inst, x=x, y=0.0) + inst = ckt.new_instance(name=inst_name, object_=cell) + nets[pad_net_name].childports += inst.ports[pad_net_name] + layouts[inst_name] = lay = layouter.place(object_=inst, x=x, y=0.0) assert lay.boundary is not None x = lay.boundary.right # 5 In/Out pairs cell_in = sky130.iolib.cells["IOPadIn"] cell_out = sky130.iolib.cells["IOPadOut"] - for i in range(5): - inst = ckt.new_instance(name=f"in{i}", object_=cell_in) - lay = layouter.place(object_=inst, x=x, y=0.0) + for i in range(stages): + conn_net_3v3 = nets[f"stage{i}_3v3"] + conn_net_1v8_in = nets[f"stage{i}_1v8"] + conn_net_1v8_out = nets[f"stage{i + 1}_1v8"] + + inst_name = f"in{i}" + inst = ckt.new_instance(name=inst_name, object_=cell_in) + nets.vss.childports += inst.ports.vss + nets.vdd.childports += inst.ports.vdd + nets.iovss.childports += inst.ports.iovss + nets.iovdd.childports += inst.ports.iovdd + conn_net_3v3.childports += inst.ports.pad + conn_net_1v8_in.childports += inst.ports.s + layouts[inst_name] = lay = layouter.place(object_=inst, x=x, y=0.0) assert lay.boundary is not None + + if i == 0: + # Insert standard cells + bottom = lay.boundary.top + 2*m4.min_space + + buf = sky130.stdcelllib.cells["buf_x4"] + assert buf.layout.boundary is not None + x2 = lay.boundary.center.x - buf.layout.boundary.right + inst_name = "roout_buf" + inst2 = ckt.new_instance(name=inst_name, object_=buf) + nets.vss.childports += inst2.ports.vss + nets.vdd.childports += inst2.ports.vdd + conn_net_1v8_in.childports += inst2.ports.i + nets.ro_out.childports += inst2.ports.q + layouts[inst_name] = lay2 = layouter.place(inst2, x=x2, y=bottom) + assert lay2.boundary is not None + x2 = lay2.boundary.right + + nand = sky130.stdcelllib.cells["nand2_x0"] + inst_name = "en_nand" + inst2 = ckt.new_instance(name=inst_name, object_=nand) + nets.vss.childports += inst2.ports.vss + nets.vdd.childports += inst2.ports.vdd + conn_net_1v8_in.childports += inst2.ports.i0 + nets.ro_en.childports += inst2.ports.i1 + nets[f"stage{stages}_1v8"].childports += inst2.ports.nq + layouts[inst_name] = lay2 = layouter.place(inst2, x=x2, y=bottom) + assert lay2.boundary is not None + x2 = lay2.boundary.right + + tie = sky130.stdcelllib.cells["tie"] + inst_name = "tie0" + inst2 = ckt.new_instance(name=inst_name, object_=tie) + nets.vss.childports += inst2.ports.vss + nets.vdd.childports += inst2.ports.vdd + layouts[inst_name] = lay2 = layouter.place(inst2, x=x2, y=bottom) + assert lay2.boundary is not None + x2 = lay2.boundary.right + inst_name = "tie1" + inst2 = ckt.new_instance(name=inst_name, object_=tie) + nets.vss.childports += inst2.ports.vss + nets.vdd.childports += inst2.ports.vdd + layouts[inst_name] = lay2 = layouter.place(inst2, x=x2, y=bottom) + x = lay.boundary.right - inst = ckt.new_instance(name=f"out{i}", object_=cell_out) - lay = layouter.place(object_=inst, x=x, y=0.0) + inst_name = f"out{i}" + inst = ckt.new_instance(name=inst_name, object_=cell_out) + nets.vss.childports += inst.ports.vss + nets.vdd.childports += inst.ports.vdd + nets.iovss.childports += inst.ports.iovss + nets.iovdd.childports += inst.ports.iovdd + conn_net_3v3.childports += inst.ports.pad + conn_net_1v8_out.childports += inst.ports.d + nets.ro_de.childports += inst.ports.de + layouts[inst_name] = lay = layouter.place(object_=inst, x=x, y=0.0) assert lay.boundary is not None x = lay.boundary.right # Last DC cells - for cell_name in ("IOPadVdd", "IOPadVss", "IOPadIOVdd", "IOPadIOVss"): + for cell_name, pad_net_name in ( + ("IOPadVdd", "vdd"), + ("IOPadVss", "vss"), + ("IOPadIOVdd", "iovdd"), + ("IOPadIOVss", "iovss"), + ): + inst_name = f"{cell_name}1" cell = sky130.iolib.cells[cell_name] - inst = ckt.new_instance(name=f"{cell_name}1", object_=cell) - lay = layouter.place(object_=inst, x=x, y=0.0) + inst = ckt.new_instance(name=inst_name, object_=cell) + nets[pad_net_name].childports += inst.ports[pad_net_name] + layouts[inst_name] = lay = layouter.place(object_=inst, x=x, y=0.0) assert lay.boundary is not None x = lay.boundary.right - assert lay is not None + assert (lay is not None) and (lay.boundary is not None) + block_top = lay.boundary.top + cell_width = x - assert isinstance(lay.boundary, _geo.Rect) - layouter.layout.boundary = _geo.Rect.from_rect(rect=lay.boundary, left=0.0) + # Add DC pins and connect to pads + for i, (cell_name, pad_net) in enumerate(( + ("IOPadVdd", nets.vdd), + ("IOPadVss", nets.vss), + ("IOPadIOVdd", nets.iovdd), + ("IOPadIOVss", nets.iovss), + )): + m4_top = -_conn_space - i*_conn_pitch + m4_bottom = m4_top - _conn_width + rect = _geo.Rect(left=0.0, bottom=m4_bottom, right=cell_width, top=m4_top) + layouter.add_wire(net=pad_net, wire=m4, pin=m4pin, shape=rect) + + for j in range(2): + inst_name = f"{cell_name}{j}" + lay = layouts[inst_name] + m5pin_bounds = lay.bounds(net=pad_net, mask=m5pin.mask) + + _l_via4 = layouter.wire_layout( + net=pad_net, wire=via4, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m4_bounds = _l_via4.bounds(mask=m4.mask) + x = m5pin_bounds.center.x + y = m4_top - _m4_bounds.top + l_via4 = layouter.place(_l_via4, x=x, y=y) + via4m5_bounds = l_via4.bounds(mask=m5.mask) + layouter.add_wire(net=pad_net, wire=m5, shape=_geo.Rect.from_rect( + rect=via4m5_bounds, top=m5pin_bounds.top, + )) + cell_bottom = -_conn_space - (4 - 1)*_conn_pitch + + tie0_bndry = layouts["tie0"].boundary + assert tie0_bndry is not None + + # Draw output pins + dem2_bottom = tie0_bndry.top + _conn_space + dem2_top = dem2_bottom + _conn_width + dem2_rect = _geo.Rect(left=0.0, bottom=dem2_bottom, right=cell_width, top=dem2_top) + layouter.add_wire(net=nets.ro_de, wire=m2, pin=m2pin, shape=dem2_rect) + + roen_bottom = dem2_top + _conn_space + roen_top = roen_bottom + _conn_width + roen_rect = _geo.Rect(left=0.0, bottom=roen_bottom, right=cell_width, top=roen_top) + layouter.add_wire(net=nets.ro_en, wire=m2, pin=m2pin, shape=roen_rect) + + roout_bottom = roen_top + _conn_space + cell_top = roout_top = roout_bottom + _conn_width + roout_rect = _geo.Rect(left=0.0, bottom=roout_bottom, right=cell_width, top=roout_top) + layouter.add_wire(net=nets.ro_out, wire=m2, pin=m2pin, shape=roout_rect) + + # Connect de to pin + _l_de_via = layouter.wire_layout( + net=nets.ro_de, wire=via, bottom_height=_conn_width, top_height=_conn_width, + ) + _m1_bounds = _l_de_via.bounds(mask=m1.mask) + s = dem2_bottom - block_top + rect = _geo.Rect.from_rect(rect=_m1_bounds, bottom=(_m1_bounds.bottom - s)) + _l_de_via.add_shape(prim=m1, net=nets.ro_de, shape=rect) + y_de_via = block_top - rect.bottom + for i in range(stages): + conn_net_3v3 = nets[f"stage{i}_3v3"] + conn_net_1v8_in = nets[f"stage{i}_1v8"] + conn_net_1v8_out = nets[f"stage{i + 1}_1v8"] + + inst_name = f"in{i}" + lay = layouts[inst_name] + inm5pin_bounds = lay.bounds(mask=m5pin.mask, net=conn_net_3v3) + + inst_name = f"out{i}" + lay = layouts[inst_name] + outm5pin_bounds = lay.bounds(mask=m5pin.mask, net=conn_net_3v3) + dem1pin_bounds = lay.bounds(mask=m1pin.mask, net=nets.ro_de) + assert abs(dem1pin_bounds.top - block_top) < tech.grid + x = dem1pin_bounds.center.x + y = y_de_via + layouter.place(_l_de_via, x=x, y=y) + + rect = _geo.Rect( + left=inm5pin_bounds.right, bottom=inm5pin_bounds.bottom, + right=outm5pin_bounds.left, top=inm5pin_bounds.bottom + _conn_width, + ) + layouter.add_wire(net=conn_net_3v3, wire=m5, shape=rect) + rect = _geo.Rect( + left=inm5pin_bounds.right, bottom=inm5pin_bounds.top - _conn_width, + right=outm5pin_bounds.left, top=inm5pin_bounds.top, + ) + layouter.add_wire(net=conn_net_3v3, wire=m5, shape=rect) + + # Connect ro_out + net = nets.ro_out + rooutlipin_bounds = layouts["roout_buf"].bounds(mask=lipin.mask, net=net, depth=1) + _l_mcon = layouter.wire_layout(net=net, wire=mcon, rows=3) + _li_bounds = _l_mcon.bounds(mask=li.mask) + x = rooutlipin_bounds.center.x + y = rooutlipin_bounds.top - _li_bounds.top + l_mcon = layouter.place(_l_mcon, x=x, y=y) + rooutmconm1_bounds = l_mcon.bounds(mask=m1.mask) + _l_via = layouter.wire_layout(net=net, wire=via, top_height=_conn_width) + _m2_bounds = _l_via.bounds(mask=m2.mask) + x = rooutlipin_bounds.center.x + y = roout_bottom - _m2_bounds.bottom + l_via = layouter.place(_l_via, x=x, y=y) + rooutviam1_bounds = l_via.bounds(mask=m1.mask) + rect = _geo.Rect.from_rect(rect=rooutviam1_bounds, bottom=rooutmconm1_bounds.bottom) + layouter.add_wire(net=net, wire=m1, shape=rect) + + # Connect ro_en + net = nets.ro_en + roenlipin_bounds = layouts["en_nand"].bounds(mask=lipin.mask, net=net, depth=1) + _l_mcon = layouter.wire_layout(net=net, wire=mcon, rows=3) + _li_bounds = _l_mcon.bounds(mask=li.mask) + x = roenlipin_bounds.center.x + y = roenlipin_bounds.top - _li_bounds.top + l_mcon = layouter.place(_l_mcon, x=x, y=y) + roenmconm1_bounds = l_mcon.bounds(mask=m1.mask) + _l_via = layouter.wire_layout(net=net, wire=via, top_height=_conn_width) + _m2_bounds = _l_via.bounds(mask=m2.mask) + x = roenmconm1_bounds.left - _m2_bounds.right + y = roen_bottom - _m2_bounds.bottom + l_via = layouter.place(_l_via, x=x, y=y) + roenviam1_bounds = l_via.bounds(mask=m1.mask) + rect = _geo.Rect.from_rect(rect=roenviam1_bounds, bottom=roenmconm1_bounds.bottom) + layouter.add_wire(net=net, wire=m1, shape=rect) + + # Connect stage0_1v8 + net = nets.stage0_1v8 + + padm1pin_bounds = layouts["in0"].bounds(net=net, mask=m1pin.mask, depth=1) + buflipin_bounds = layouts["roout_buf"].bounds(net=net, mask=lipin.mask, depth=1) + nandlipin_bounds = layouts["en_nand"].bounds(net=net, mask=lipin.mask, depth=1) + # Check assumption of placement of cells with regard to pad pin + assert buflipin_bounds.center.x < padm1pin_bounds.center.x < nandlipin_bounds.center.x + + _l_mcon = layouter.wire_layout(net=net, wire=mcon, rows=2) + _li_bounds = _l_mcon.bounds(mask=li.mask) + bottom = max(buflipin_bounds.bottom, nandlipin_bounds.bottom) + y = bottom - _li_bounds.bottom + l_mcon1 = layouter.place(_l_mcon, x=buflipin_bounds.center.x, y=y) + bufm1_bounds = l_mcon1.bounds(mask=m1.mask) + l_mcon2 = layouter.place(_l_mcon, x=nandlipin_bounds.center.x, y=y) + nandm1_bounds = l_mcon2.bounds(mask=m1.mask) + shape = _geo.Polygon.from_floats(points=( + (padm1pin_bounds.left, padm1pin_bounds.top), + (padm1pin_bounds.left, bottom), + (bufm1_bounds.left, bottom), + (bufm1_bounds.left, bufm1_bounds.top), + (nandm1_bounds.right, bufm1_bounds.top), + (nandm1_bounds.right, bottom), + (padm1pin_bounds.right, bottom), + (padm1pin_bounds.right, padm1pin_bounds.top), + (padm1pin_bounds.left, padm1pin_bounds.top), + )) + layouter.add_wire(net=net, wire=m1, shape=shape) + + # Connect stage{stages}_1v8 + net = nets[f"stage{stages}_1v8"] + + padm1pin_bounds = layouts[f"out{stages - 1}"].bounds(net=net, mask=m1pin.mask, depth=1) + nandlipin_bounds = layouts["en_nand"].bounds(net=net, mask=lipin.mask) + # Check assumptions of placement of pin + assert nandlipin_bounds.right < padm1pin_bounds.left + + _l_mcon = layouter.wire_layout(net=net, wire=mcon, rows=2) + _li_bounds = _l_mcon.bounds(mask=li.mask) + x = nandlipin_bounds.center.x + y = nandlipin_bounds.top - _li_bounds.top + l_mcon = layouter.place(_l_mcon, x=x, y=y) + nandm1_bounds = l_mcon.bounds(mask=m1.mask) + x = nandm1_bounds.center.x + y = nandm1_bounds.center.y + l1_via = layouter.add_wire(net=net, wire=via, rows=2, x=x, y=y) + m2_bounds1 = l1_via.bounds(mask=m2.mask) + x = padm1pin_bounds.center.x + l2_via = layouter.add_wire(net=net, wire=via, rows=2, x=x, y=y) + m1_bounds2 = l2_via.bounds(mask=m1.mask) + m2_bounds2 = l2_via.bounds(mask=m2.mask) + rect = _geo.Rect.from_rect(rect=m2_bounds1, right=m2_bounds2.right) + layouter.add_wire(net=net, wire=m2, shape=rect) + rect = _geo.Rect.from_rect(rect=padm1pin_bounds, top=m1_bounds2.top) + layouter.add_wire(net=net, wire=m1, shape=rect) + + layouter.add_wire(net=net, wire=m1, shape=shape) + + # Connect stage{n}_1v8 n=1..(stages - 1) + for i in range(1, stages): + net = nets[f"stage{i}_1v8"] + + outpadm1pin_bounds = layouts[f"out{i - 1}"].bounds(net=net, mask=m1pin.mask, depth=1) + inpadm1pin_bounds = layouts[f"in{i}"].bounds(net=net, mask=m1pin.mask, depth=1) + # Check assumptions + assert outpadm1pin_bounds.right < inpadm1pin_bounds.left + + _l_via = layouter.wire_layout(net=net, wire=via, rows=2) + _m2_bounds = _l_via.bounds(mask=m2.mask) + y = block_top + 2*m2.min_space - _m2_bounds.bottom + l1_via = layouter.place(_l_via, x=outpadm1pin_bounds.center.x, y=y) + m1_bounds1 = l1_via.bounds(mask=m1.mask) + m2_bounds1 = l1_via.bounds(mask=m2.mask) + l2_via = layouter.place(_l_via, x=inpadm1pin_bounds.center.x, y=y) + m1_bounds2 = l2_via.bounds(mask=m1.mask) + m2_bounds2 = l2_via.bounds(mask=m2.mask) + + rect = _geo.Rect.from_rect(rect=outpadm1pin_bounds, top=m1_bounds1.top) + layouter.add_wire(net=net, wire=m1, shape=rect) + rect = _geo.Rect.from_rect(rect=inpadm1pin_bounds, top=m1_bounds2.top) + layouter.add_wire(net=net, wire=m1, shape=rect) + rect = _geo.Rect.from_rect(rect=m2_bounds1, right=m2_bounds2.right) + layouter.add_wire(net=net, wire=m2, shape=rect) + + # Connect vss/vdd of standard cells + net = nets.vss + vsstielipin_bounds = layouts["tie0"].bounds(net=net, mask=lipin.mask) + vsspadm4pin_bounds = layouts["in0"].bounds(net=net, mask=m4pin.mask) + x = vsstielipin_bounds.center.x + y = vsstielipin_bounds.center.y + h = vsstielipin_bounds.height + for via_layer in (mcon, via, via2, via3): + l_via = layouter.add_wire( + net=net, wire=via_layer, bottom_height=h, top_height=h, x=x, y=y, + ) + m4_bounds = l_via.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=m4_bounds, bottom=vsspadm4pin_bounds.bottom) + layouter.add_wire(net=net, wire=m4, shape=rect) + + net = nets.vdd + vddtielipin_bounds = layouts["tie1"].bounds(net=net, mask=lipin.mask) + vddpadm4pin_bounds = layouts["in0"].bounds(net=net, mask=m4pin.mask) + x = vddtielipin_bounds.center.x + y = vddtielipin_bounds.center.y + h = vddtielipin_bounds.height + for via_layer in (mcon, via, via2, via3): + l_via = layouter.add_wire( + net=net, wire=via_layer, bottom_height=h, top_height=h, x=x, y=y, + ) + # via4 needs bigger line + l_via4 = layouter.add_wire(net=net, wire=via4, rows=2, x=x, y=y) + m5_bounds1 = l_via4.bounds(mask=m5.mask) + _l_via4 = layouter.wire_layout(net=net, wire=via4, rows=2) + _m4_bounds = _l_via4.bounds(mask=m4.mask) + y = vddpadm4pin_bounds.top - _m4_bounds.top + l_via4 = layouter.place(_l_via4, x=x, y=y) + m5_bounds2 = l_via4.bounds(mask=m5.mask) + rect = _geo.Rect.from_rect(rect=m5_bounds1, bottom=m5_bounds2.bottom) + layouter.add_wire(net=net, wire=m5, shape=rect) + + # Set set boundary + bndry = _geo.Rect(left=0.0, bottom=cell_bottom, right=cell_width, top=cell_top) + layouter.layout.boundary = bndry return ioro @@ -99,25 +598,472 @@ def _gen_ioconn(*, lib: _lib.Library, ioblock: _lib._Cell[_lib.Library], ioro: _lib._Cell[_lib.Library], ): + tech = lib.tech + prims = tech.primitives + + m1 = cast(_prm.MetalWire, prims.m1) + assert (m1.pin is not None) and (len(m1.pin) == 1) + m1pin = m1.pin[0] + via = cast(_prm.Via, prims.via) + m2 = cast(_prm.MetalWire, prims.m2) + assert (m2.pin is not None) and (len(m2.pin) == 1) + m2pin = m2.pin[0] + via2 = cast(_prm.Via, prims.via2) + m3 = cast(_prm.MetalWire, prims.m3) + assert (m3.pin is not None) and (len(m3.pin) == 1) + m3pin = m3.pin[0] + via3 = cast(_prm.Via, prims.via3) + m4 = cast(_prm.MetalWire, prims.m4) + assert (m4.pin is not None) and (len(m4.pin) == 1) + m4pin = m4.pin[0] + via4 = cast(_prm.Via, prims.via4) + m5 = cast(_prm.MetalWire, prims.m5) + assert (m5.pin is not None) and (len(m5.pin) == 1) + m5pin = m5.pin[0] + + conn_top = 3500.0 + # Create connected out cell ioconn = lib.new_cell(name="IOConnected") ckt = ioconn.new_circuit() + nets = ckt.nets layouter = ioconn.new_circuitlayouter() + # Create the top nets + for net_name in _netlookup.keys(): + ckt.new_net(name=net_name, external=True) + + # assign nets to different horizontal m4 lanes, some at same height + n_conn = { + "iovdd": 0, + "vdd": 0, + "ioin_pad": 1, + "ioout_pad": 1, + "ioin_core": 2, + "ioinout_pad": 2, + "ioinout_core": 3, + "d_core": 4, + "de_core": 4, + } + conns = max(n_conn.values()) + 1 + blk_top = conn_top - conns*_conn_pitch + inst = ckt.new_instance(name="block", object_=ioblock) + for net_name in _netlookup.keys(): + if net_name.startswith("ro_"): + continue + nets[net_name].childports += inst.ports[net_name] bound = ioblock.layout.boundary assert isinstance(bound, _geo.Rect) x = _frm.boundary.center.x - bound.center.x - y = _frm.boundary.top - 500.0 - layouter.place(object_=inst, x=x, y=y) + y = blk_top - bound.top + l_block = layouter.place(inst, x=x, y=y) + + # Connect other bond pads + for net_name in ("iovdd", "ioin_pad", "ioinout_pad", "ioout_pad", "vdd"): + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + blkpinm5_bounds = l_block.bounds(net=net, mask=m5pin.mask) + lane = n_conn[net_name] + m4_top = conn_top - lane*_conn_pitch + conn_right = pinrect.right < blkpinm5_bounds.left + if not conn_right: + assert pinrect.left > blkpinm5_bounds.right + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + _l_via3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m4_bounds = _l_via3.bounds(mask=m4.mask) + x = pinrect.center.x + y = m4_top - _m4_bounds.top + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + layouter.add_wire(net=net, wire=m3, shape=_geo.Rect.from_rect( + rect=via3m3_bounds, top=pinrect.bottom, + )) + _l_via4 = layouter.wire_layout( + net=net, wire=via4, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m4_bounds = _l_via4.bounds(mask=m4.mask) + _m5_bounds = _l_via4.bounds(mask=m5.mask) + x = blkpinm5_bounds.center.x + y = m4_top - _m4_bounds.top + l_via4 = layouter.place(_l_via4, x=x, y=y) + via4m4_bounds = l_via4.bounds(mask=m4.mask) + via4m5_bounds = l_via4.bounds(mask=m5.mask) + if conn_right: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, right=via4m4_bounds.right, + )) + else: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, left=via4m4_bounds.left, + )) + layouter.add_wire(net=net, wire=m5, shape=_geo.Rect.from_rect( + rect=via4m5_bounds, bottom=blkpinm5_bounds.bottom, + )) + + # Connect core pins + for net_name in ("ioin_core", "ioinout_core"): + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + blkpinm1_bounds = l_block.bounds(net=net, mask=m1pin.mask) + blkm2_bounds = l_block.bounds(mask=m2.mask) + lane = n_conn[net_name] + m4_top = conn_top - lane*_conn_pitch + conn_right = pinrect.right < blkpinm1_bounds.left + if not conn_right: + assert pinrect.left > blkpinm1_bounds.right + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + _l_via3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m4_bounds = _l_via3.bounds(mask=m4.mask) + x = pinrect.center.x + y = m4_top - _m4_bounds.top + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + layouter.add_wire(net=net, wire=m3, shape=_geo.Rect.from_rect( + rect=via3m3_bounds, top=pinrect.bottom, + )) + _l_blkvia = layouter.wire_layout( + net=net, wire=via, columns=10, rows=10, + ) + _m2_bounds = _l_blkvia.bounds(mask=m2.mask) + x = blkpinm1_bounds.center.x + y = blkm2_bounds.top + 2*m2.min_space - _m2_bounds.bottom + l_blkvia = layouter.place(_l_blkvia, x=x, y=y) + blkviam1_bounds = l_blkvia.bounds(mask=m2.mask) + blkviam2_bounds = l_blkvia.bounds(mask=m2.mask) + layouter.add_wire(net=net, wire=m1, shape=_geo.Rect.from_rect( + rect=blkpinm1_bounds, top=blkviam1_bounds.top, + )) + _l_blkvia2 = layouter.wire_layout( + net=net, wire=via2, columns=20, rows=20, + ) + _m2_bounds = _l_blkvia2.bounds(mask=m2.mask) + x = blkpinm1_bounds.center.x + y = blkviam2_bounds.top - _m2_bounds.bottom + l_blkvia2 = layouter.place(_l_blkvia2, x=x, y=y) + blkvia2m3_bounds = l_blkvia2.bounds(mask=m3.mask) + layouter.add_wire(net=net, wire=m3, shape=_geo.Rect.from_rect( + rect=blkvia2m3_bounds, top=m4_top, + )) + _l_blkvia3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=blkvia2m3_bounds.width, bottom_height=_conn_width, + top_width=blkvia2m3_bounds.width, top_height=_conn_width, + ) + _m4_bounds = _l_blkvia3.bounds(mask=m4.mask) + x = blkpinm1_bounds.center.x + y = m4_top - _m4_bounds.top + l_blkvia3 = layouter.place(_l_blkvia3, x=x, y=y) + blkvia3m4_bounds = l_blkvia3.bounds(mask=m4.mask) + if conn_right: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, right=blkvia3m4_bounds.right, + )) + else: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, left=blkvia3m4_bounds.left, + )) + + # Connect block m2 pins + for net_name in ("d_core", "de_core"): + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + blkpinm2_bounds = l_block.bounds(net=net, mask=m2pin.mask) + lane = n_conn[net_name] + m4_top = conn_top - lane*_conn_pitch + conn_right = pinrect.right < blkpinm2_bounds.left + if not conn_right: + assert pinrect.left > blkpinm2_bounds.right + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + _l_via3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m4_bounds = _l_via3.bounds(mask=m4.mask) + x = pinrect.center.x + y = m4_top - _m4_bounds.top + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + layouter.add_wire(net=net, wire=m3, shape=_geo.Rect.from_rect( + rect=via3m3_bounds, top=pinrect.bottom, + )) + _l_blkvia2 = layouter.wire_layout(net=net, wire=via2, columns=20, bottom_enclosure="wide") + _m2_bounds = _l_blkvia2.bounds(mask=m2.mask) + if conn_right: + x = blkpinm2_bounds.left - _m2_bounds.left + else: + x = blkpinm2_bounds.right - _m2_bounds.right + y = blkpinm2_bounds.center.y + l_blkvia2 = layouter.place(_l_blkvia2, x=x, y=y) + blkvia2m3_bounds = l_blkvia2.bounds(mask=m3.mask) + layouter.add_wire(net=net, wire=m3, shape=_geo.Rect.from_rect( + rect=blkvia2m3_bounds, top=m4_top, + )) + _l_blkvia3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=blkvia2m3_bounds.width, bottom_height=_conn_width, + top_width=blkvia2m3_bounds.width, top_height=_conn_width, + ) + _m4_bounds = _l_blkvia3.bounds(mask=m4.mask) + x = blkvia2m3_bounds.center.x + y = m4_top - _m4_bounds.top + l_blkvia3 = layouter.place(_l_blkvia3, x=x, y=y) + blkvia3m4_bounds = l_blkvia3.bounds(mask=m4.mask) + if conn_right: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, right=blkvia3m4_bounds.right, + )) + else: + layouter.add_wire(net=net, wire=m4, shape=_geo.Rect.from_rect( + rect=via3m4_bounds, left=blkvia3m4_bounds.left, + )) inst = ckt.new_instance(name="ro", object_=ioro) + for net_name in _netlookup.keys(): + if not ( + net_name.startswith("ro_") + or net_name.endswith("vss") + or net_name.endswith("vdd") + ): + continue + nets[net_name].childports += inst.ports[net_name] bound = ioro.layout.boundary assert isinstance(bound, _geo.Rect) x = _frm.boundary.center.x - bound.center.x - y = _frm.boundary.top - 1500.0 - layouter.place(object_=inst, x=x, y=y) + assert l_block.boundary is not None + y = l_block.boundary.bottom - _conn_space - bound.top + l_ro = layouter.place(object_=inst, x=x, y=y) + # Connect iovss + net_name = "iovss" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + blkpinm5_bounds = l_block.bounds(net=net, mask=m5pin.mask, depth=1) + ropinm4_bounds = l_ro.bounds(net=net, mask=m4pin.mask, depth=1) + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + + _l_via3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m3_bounds = _l_via3.bounds(mask=m3.mask) + _m4_bounds = _l_via3.bounds(mask=m4.mask) + x = pinrect.right + _conn_width - _m3_bounds.left + y = blkpinm5_bounds.top - _m4_bounds.top + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=via3m4_bounds, right=blkpinm5_bounds.left) + layouter.add_wire(net=net, wire=m4, shape=rect) + y = ropinm4_bounds.center.y + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=ropinm4_bounds, left=via3m4_bounds.left) + layouter.add_wire(net=net, wire=m4, shape=rect) + + shape = _geo.Polygon.from_floats(points=( + (via3m3_bounds.left, via3m3_bounds.bottom), + (via3m3_bounds.left, pinrect.bottom), + (pinrect.left, pinrect.bottom), + (pinrect.left, pinrect.top), + (via3m3_bounds.right, pinrect.top), + (via3m3_bounds.right, via3m3_bounds.bottom), + (via3m3_bounds.left, via3m3_bounds.bottom), + )) + layouter.add_wire(net=net, wire=m3, shape=shape) + + # Connect vss + net_name = "vss" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + blkpinm5_bounds = l_block.bounds(net=net, mask=m5pin.mask, depth=1) + ropinm4_bounds = l_ro.bounds(net=net, mask=m4pin.mask, depth=1) + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + + _l_via3 = layouter.wire_layout( + net=net, wire=via3, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + _m3_bounds = _l_via3.bounds(mask=m3.mask) + _m4_bounds = _l_via3.bounds(mask=m4.mask) + x = pinrect.left - _conn_width - _m3_bounds.right + y = blkpinm5_bounds.top - _m4_bounds.top + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=via3m4_bounds, left=blkpinm5_bounds.right) + layouter.add_wire(net=net, wire=m4, shape=rect) + y = ropinm4_bounds.center.y + l_via3 = layouter.place(_l_via3, x=x, y=y) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=ropinm4_bounds, right=via3m4_bounds.right) + layouter.add_wire(net=net, wire=m4, shape=rect) + + shape = _geo.Polygon.from_floats(points=( + (via3m3_bounds.left, via3m3_bounds.bottom), + (via3m3_bounds.left, pinrect.top), + (pinrect.left, pinrect.top), + (pinrect.left, pinrect.bottom), + (via3m3_bounds.right, pinrect.bottom), + (via3m3_bounds.right, via3m3_bounds.bottom), + (via3m3_bounds.left, via3m3_bounds.bottom), + )) + layouter.add_wire(net=net, wire=m3, shape=shape) + + # Connect second iovdd + net_name = "iovdd" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + ropinm4_bounds = l_ro.bounds(net=net, mask=m4pin.mask, depth=1) + + l_via3 = layouter.add_wire( + net=net, wire=via3, x=pinrect.center.x, y=ropinm4_bounds.center.y, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=pinrect, bottom=via3m3_bounds.bottom) + layouter.add_wire(net=net, wire=m3, shape=rect) + rect = _geo.Rect.from_rect(rect=ropinm4_bounds, left=via3m4_bounds.left) + layouter.add_wire(net=net, wire=m4, shape=rect) + + # Connect second vdd + net_name = "vdd" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + ropinm4_bounds = l_ro.bounds(net=net, mask=m4pin.mask, depth=1) + + l_via3 = layouter.add_wire( + net=net, wire=via3, x=pinrect.center.x, y=ropinm4_bounds.center.y, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + via3m3_bounds = l_via3.bounds(mask=m3.mask) + via3m4_bounds = l_via3.bounds(mask=m4.mask) + rect = _geo.Rect.from_rect(rect=pinrect, bottom=via3m3_bounds.bottom) + layouter.add_wire(net=net, wire=m3, shape=rect) + rect = _geo.Rect.from_rect(rect=ropinm4_bounds, right=via3m4_bounds.right) + layouter.add_wire(net=net, wire=m4, shape=rect) + + # Connect ro_en + net_name = "ro_en" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + ropinm2_bounds = l_ro.bounds(net=net, mask=m2pin.mask, depth=1) + # Assumptions + assert pinrect.top < ropinm2_bounds.bottom + assert pinrect.left > ropinm2_bounds.right + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + + x = pinrect.left - _conn_width + l_via2 = layouter.add_wire( + net=net, wire=via2, x=x, y=pinrect.center.y, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + m2_bounds = l_via2.bounds(mask=m2.mask) + m3_bounds = l_via2.bounds(mask=m3.mask) + rect = _geo.Rect.from_rect(rect=pinrect, left=m3_bounds.left) + layouter.add_wire(net=net, wire=m3, shape=rect) + shape = _geo.Polygon.from_floats(points=( + (m2_bounds.left, m2_bounds.bottom), + (m2_bounds.left, ropinm2_bounds.bottom), + (ropinm2_bounds.left, ropinm2_bounds.bottom), + (ropinm2_bounds.left, ropinm2_bounds.top), + (m2_bounds.right, ropinm2_bounds.top), + (m2_bounds.right, m2_bounds.bottom), + (m2_bounds.left, m2_bounds.bottom), + )) + layouter.add_wire(net=net, wire=m2, shape=shape) + + # Connect ro_de + net_name = "ro_de" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + ropinm2_bounds = l_ro.bounds(net=net, mask=m2pin.mask, depth=1) + # Assumptions + assert pinrect.top < ropinm2_bounds.bottom + assert pinrect.left > ropinm2_bounds.right + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + + x = pinrect.left - _conn_pitch - _conn_width + l_via2 = layouter.add_wire( + net=net, wire=via2, x=x, y=pinrect.center.y, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + m2_bounds = l_via2.bounds(mask=m2.mask) + m3_bounds = l_via2.bounds(mask=m3.mask) + rect = _geo.Rect.from_rect(rect=pinrect, left=m3_bounds.left) + layouter.add_wire(net=net, wire=m3, shape=rect) + shape = _geo.Polygon.from_floats(points=( + (m2_bounds.left, m2_bounds.bottom), + (m2_bounds.left, ropinm2_bounds.bottom), + (ropinm2_bounds.left, ropinm2_bounds.bottom), + (ropinm2_bounds.left, ropinm2_bounds.top), + (m2_bounds.right, ropinm2_bounds.top), + (m2_bounds.right, m2_bounds.bottom), + (m2_bounds.left, m2_bounds.bottom), + )) + layouter.add_wire(net=net, wire=m2, shape=shape) + + # Connect ro_out + net_name = "ro_out" + net = nets[net_name] + pinrect = _frm.toppins[_netlookup[net_name]] + ropinm2_bounds = l_ro.bounds(net=net, mask=m2pin.mask, depth=1) + # Assumptions + assert pinrect.top < ropinm2_bounds.bottom + assert pinrect.right < ropinm2_bounds.left + + layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=pinrect) + + x = pinrect.right + _conn_width + l_via2 = layouter.add_wire( + net=net, wire=via2, x=x, y=pinrect.center.y, + bottom_width=_conn_width, bottom_height=_conn_width, + top_width=_conn_width, top_height=_conn_width, + ) + m2_bounds = l_via2.bounds(mask=m2.mask) + m3_bounds = l_via2.bounds(mask=m3.mask) + rect = _geo.Rect.from_rect(rect=pinrect, right=m3_bounds.right) + layouter.add_wire(net=net, wire=m3, shape=rect) + shape = _geo.Polygon.from_floats(points=( + (m2_bounds.left, m2_bounds.bottom), + (m2_bounds.left, ropinm2_bounds.top), + (ropinm2_bounds.left, ropinm2_bounds.top), + (ropinm2_bounds.left, ropinm2_bounds.bottom), + (m2_bounds.right, ropinm2_bounds.bottom), + (m2_bounds.right, m2_bounds.bottom), + (m2_bounds.left, m2_bounds.bottom), + )) + layouter.add_wire(net=net, wire=m2, shape=shape) + + # Set boundary layouter.layout.boundary = _frm.boundary return ioconn
diff --git a/gds/user_analog_project_wrapper.gds.gz b/gds/user_analog_project_wrapper.gds.gz index e111994..d9732d8 100644 --- a/gds/user_analog_project_wrapper.gds.gz +++ b/gds/user_analog_project_wrapper.gds.gz Binary files differ