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