blob: 0c9c0de6777aa28fe944b7272ada0801907157e9 [file] [log] [blame]
# SPDX-License-Identifier: GPL-2.0-or-later
# 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 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 circuit, layout as _lay, library as _lib
from pdkmaster.io.klayout import export as _klexp
from c4m.pdk import sky130
from . import frame as _frm
__all__ = ["gen_gds"]
# 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
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) + 2*m2.min_space
bottom = max(d_m1pin_bounds1.top, d_m1pin_bounds2.top) + 0.5 # big m3 space rule
_l_via = layouter.wire_layout(net=net, wire=via, rows=2)
_m1_bounds = _l_via.bounds(mask=m1.mask)
x = d_m1pin_bounds1.center.x
y = bottom - _m1_bounds.bottom
l1_via = layouter.place(_l_via, x=x, y=y)
m1_bounds1 = l1_via.bounds(mask=m1.mask)
m2_bounds1 = l1_via.bounds(mask=m2.mask)
x = d_m1pin_bounds2.center.x
l2_via = layouter.place(_l_via, 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=d_m1pin_bounds1, top=m1_bounds1.top)
layouter.add_wire(net=net, wire=m1, shape=rect)
rect = _geo.Rect.from_rect(rect=d_m1pin_bounds2, 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)
l_m2 = layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=rect)
dm2_bounds = l_m2.bounds()
net = nets.de_core
bottom = dm2_bounds.top + m2.min_space
_l_via = layouter.wire_layout(net=net, wire=via, rows=2)
_m1_bounds = _l_via.bounds(mask=m1.mask)
x = de_m1pin_bounds1.center.x
y = bottom - _m1_bounds.bottom
l1_via = layouter.place(_l_via, x=x, y=y)
m1_bounds1 = l1_via.bounds(mask=m1.mask)
m2_bounds1 = l1_via.bounds(mask=m2.mask)
x = de_m1pin_bounds2.center.x
l2_via = layouter.place(_l_via, 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=de_m1pin_bounds1, top=m1_bounds1.top)
layouter.add_wire(net=net, wire=m1, shape=rect)
rect = _geo.Rect.from_rect(rect=de_m1pin_bounds2, 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, pin=m2pin, shape=rect)
assert isinstance(lay.boundary, _geo.Rect)
layouter.layout.boundary = _geo.Rect.from_rect(rect=lay.boundary, left=0.0)
return ioblock
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, 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=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(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_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, 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=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) and (lay.boundary is not None)
block_top = lay.boundary.top
cell_width = x
# 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
# Shift a little below highest possible top to avoid being too close to vdd
# connection via
y = nandlipin_bounds.top - _li_bounds.top - m2.min_space
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
# Use 2 columns so width is > min_width
l_via4 = layouter.add_wire(net=net, wire=via4, columns=2, x=x, y=y)
m5_bounds1 = l_via4.bounds(mask=m5.mask)
_l_via4 = layouter.wire_layout(net=net, wire=via4, columns=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
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 = 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
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
def _postprocess(*, klay: pya.Layout):
# Post process
# - split tapdiff into tap and diff layers
# - remove pad drawing layer to avoid interacting with RDL
nwell_idx = klay.layer(64, 20)
diff_idx = klay.layer(65, 20)
tap_idx = klay.layer(65, 44)
nsdm_idx = klay.layer(93, 44)
psdm_idx = klay.layer(94, 20)
pad_idx = klay.layer(76, 20)
for cell in klay.each_cell():
nwell = pya.Region(cell.shapes(nwell_idx))
nsdm = pya.Region(cell.shapes(nsdm_idx))
psdm = pya.Region(cell.shapes(psdm_idx))
tap_cover = (nsdm & nwell) + (psdm - nwell)
diff_shapes = cell.shapes(diff_idx)
tap_shapes = cell.shapes(tap_idx)
difftap = pya.Region(cell.shapes(diff_idx)) # Original difftap layer to be split
tap = difftap & tap_cover
diff = difftap - tap
diff_shapes.clear()
diff_shapes.insert(diff)
tap_shapes.insert(tap)
cell.shapes(pad_idx).clear()
def gen_gds(*, gds_out: Path, gds_empty: Path):
top_name = "user_analog_project_wrapper"
lib = _lib.Library(
name="MPW4IOTest", tech=sky130.tech, cktfab=sky130.cktfab, layoutfab=sky130.layoutfab,
)
ioblock = _gen_ioblock(lib=lib)
ioro = _gen_ioro(lib=lib)
ioconn = _gen_ioconn(lib=lib, ioblock=ioblock, ioro=ioro)
# Create user_analog_project_wrapper top cell
top = lib.new_cell(name=top_name)
ckt = top.new_circuit()
layouter = top.new_circuitlayouter()
inst = ckt.new_instance(name="ioconn", object_=ioconn)
layouter.place(inst, x=0.0, y=0.0)
layouter.layout.boundary = _frm.boundary
# Convert to klayout
klay = _klexp.export2db(
obj=lib, add_pin_label=True, gds_layers=sky130.gds_layers, cell_name=None, merge=True,
)
_postprocess(klay=klay)
# Instantiate the empty cell in top cell
emptylib = pya.Library()
emptylib.register("empty")
emptylayout = emptylib.layout()
emptylayout.read(str(gds_empty))
emptylibcell_idx = emptylayout.cell("user_analog_project_wrapper_empty").cell_index()
emptycell_idx = klay.add_lib_cell(emptylib, emptylibcell_idx)
ktop = klay.cell(top_name)
ktop.insert(pya.CellInstArray(emptycell_idx, pya.Trans.R0))
ktech = pya.Technology.technology_by_name("Skywater_S8")
ksaveopts = ktech.save_layout_options.dup()
ksaveopts.write_context_info = False
klay.write(str(gds_out), ksaveopts)