blob: b0ddb51658b19828df89a30574efaf4606c3485c [file] [log] [blame]
# SPDX-License-Identifier: GPL-2.0-or-later
from typing import Optional, Tuple, Union, cast
from pdkmaster.technology import geometry as _geo, primitive as _prm
from pdkmaster.design import circuit as _ckt, library as _lbry
from c4m.pdk import sky130
from . import frame as _frm
__all__ = ["ConnectedSRAM"]
class _io_spec:
def __init__(self, *,
sram_signal: Union[str, Tuple[str, str, str]],
io_type: str, io_number: int,
):
assert io_type in ("io_in", "io_out", "io_analog")
assert (io_type != "io_inout") or isinstance(sram_signal, tuple)
self.sram_signal = sram_signal
self.io_type = io_type
self.io_number = io_number
@property
def prefix(self) -> str:
if self.io_type in ("io_in", "io_out", "io_inout"):
return f"io{self.io_number}"
elif self.io_type == "io_analog":
return f"ioa{self.io_number}"
elif self.io_type == "gpio":
return f"gpio{self.io_number}"
else:
raise NotImplementedError(f"io_type == '{self.io_type}'")
@property
def toppin_name(self) -> str:
num = self.io_number
if self.io_type == "io_in":
return f"io_in[{num}]"
elif self.io_type == "io_out":
return f"io_out[{num}]"
elif self.io_type == "io_analog":
return f"io_analog[{num}]"
else:
raise NotImplementedError(f"io_type == {self.io_type}")
@property
def oeb(self) -> Optional[bool]:
if self.io_type == "io_in":
return True
elif self.io_type == "io_out":
return False
else:
return None
io_specs = (
_io_spec(sram_signal="a[8]", io_type="io_in", io_number=14),
_io_spec(sram_signal="a[7]", io_type="io_in", io_number=15),
_io_spec(sram_signal="a[6]", io_type="io_in", io_number=16),
_io_spec(sram_signal="a[5]", io_type="io_in", io_number=17),
_io_spec(sram_signal="a[4]", io_type="io_in", io_number=18),
_io_spec(sram_signal="a[3]", io_type="io_in", io_number=19),
_io_spec(sram_signal="a[2]", io_type="io_in", io_number=20),
_io_spec(sram_signal="a[1]", io_type="io_in", io_number=21),
_io_spec(sram_signal="a[0]", io_type="io_in", io_number=22),
_io_spec(sram_signal="clk", io_type="io_in", io_number=23),
_io_spec(sram_signal="q[7]", io_type="io_out", io_number=13),
_io_spec(sram_signal="d[7]", io_type="io_in", io_number=12),
_io_spec(sram_signal="d[6]", io_type="io_in", io_number=11),
_io_spec(sram_signal="q[6]", io_type="io_out", io_number=10),
_io_spec(sram_signal="q[5]", io_type="io_out", io_number=9),
_io_spec(sram_signal="d[5]", io_type="io_in", io_number=8),
_io_spec(sram_signal="d[4]", io_type="io_in", io_number=7),
_io_spec(sram_signal="q[4]", io_type="io_out", io_number=6),
_io_spec(sram_signal="q[3]", io_type="io_out", io_number=5),
_io_spec(sram_signal="d[3]", io_type="io_in", io_number=4),
_io_spec(sram_signal="d[2]", io_type="io_in", io_number=3),
_io_spec(sram_signal="q[2]", io_type="io_out", io_number=2),
_io_spec(sram_signal="q[1]", io_type="io_out", io_number=1),
_io_spec(sram_signal="we[0]", io_type="io_in", io_number=0),
_io_spec(sram_signal="d[1]", io_type="io_in", io_number=26),
_io_spec(sram_signal="d[0]", io_type="io_in", io_number=25),
_io_spec(sram_signal="q[0]", io_type="io_out", io_number=24),
_io_spec(sram_signal="vdd", io_type="io_analog", io_number=4),
_io_spec(sram_signal="vss", io_type="io_analog", io_number=5),
)
io_sig2spec = {
spec.sram_signal: spec
for spec in io_specs
}
io_pin2spec = {
spec.toppin_name: spec
for spec in io_specs
}
class ConnectedSRAM(_lbry._Cell[_lbry.Library]):
def __init__(self, *, lib: _lbry.Library):
super().__init__(lib=lib, name="ConnectedSRAM")
tech = lib.tech
prims = tech.primitives
stdcells = sky130.stdcelllib.cells
mem_fab = sky130.Sky130SP6TFactory(lib=self.lib)
nwm = cast(_prm.Well, prims.nwm)
li = cast(_prm.MetalWire, prims.li)
lipin = cast(_prm.Marker, prims["li.pin"])
mcon = cast(_prm.Via, prims.mcon)
m1 = cast(_prm.MetalWire, prims.m1)
m1pin = cast(_prm.Marker, prims["m1.pin"])
via = cast(_prm.Via, prims.via)
m2 = cast(_prm.MetalWire, prims.m2)
m2pin = cast(_prm.Marker, prims["m2.pin"])
via2 = cast(_prm.Via, prims.via2)
m3 = cast(_prm.MetalWire, prims.m3)
m3pin = cast(_prm.Marker, prims["m3.pin"])
zero_cell = stdcells.zero_x1
zero_nets = zero_cell.circuit.nets
_zero_zerolipinbb = zero_cell.layout.bounds(
mask=lipin.mask, net=zero_nets.zero, depth=1,
)
_zero_bb = zero_cell.layout.boundary
assert _zero_bb is not None
one_cell = stdcells.one_x1
_one_bb = one_cell.layout.boundary
assert _one_bb is not None
buf_cell = stdcells.buf_x2
buf_nets = buf_cell.circuit.nets
_buf_ilipinbb = buf_cell.layout.bounds(mask=lipin.mask, net=buf_nets.i, depth=1)
_buf_bb = buf_cell.layout.boundary
assert _buf_bb is not None
tie_cell = stdcells.tie_diff_w4
_tie_bb = tie_cell.layout.boundary
assert _tie_bb is not None
sram_cell = mem_fab.block(words=512, word_size=8, we_size=1)
self.a_bits = sram_cell.a_bits
self.word_size = sram_cell.word_size
ckt = self.new_circuit()
layouter = self.new_circuitlayouter()
layout = layouter.layout
# vss/vdd for the included standard cells
#
dvss_name = "vssd1"
dvss = ckt.new_net(name=dvss_name, external=True)
dvss_bb = _frm.toppins[dvss_name]
dvdd_name = "vccd1"
dvdd = ckt.new_net(name=dvdd_name, external=True)
dvdd_bb = _frm.toppins[dvdd_name]
# Place the SRAM
#
# instantiate
sram = ckt.instantiate(sram_cell, name="sram")
# place
_sram_lay = layouter.inst_layout(inst=sram)
_sram_bb = _sram_lay.boundary
assert _sram_bb is not None
x = sky130.tech.on_grid(_frm.boundary.center.x - _sram_bb.center.x)
y = _frm.boundary.top - 100.0 - _sram_bb.top
sram_lay = layouter.place(_sram_lay, x=x, y=y)
sram_bb = sram_lay.boundary
assert sram_bb is not None
# Make three rows of to place standard cells in
#
dbound = 4.0
# left
rot_leftrow = _geo.Rotation.R90
_tie_rotbb = rot_leftrow*_tie_bb
x_leftrow = dbound - _tie_rotbb.left
inst = ckt.instantiate(tie_cell, name="lltie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
y_tie = dbound - _tie_rotbb.bottom
lay = layouter.place(inst, x=x_leftrow, y=y_tie, rotation=rot_leftrow)
nwmbb1 = lay.bounds(mask=nwm.mask)
lipindvssbb1 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb1 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
inst = ckt.instantiate(tie_cell, name="ultie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
y_tie = _frm.boundary.top - dbound - _tie_rotbb.top
lay = layouter.place(inst, x=x_leftrow, y=y_tie, rotation=rot_leftrow)
nwmbb2 = lay.bounds(mask=nwm.mask)
lipindvssbb2 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb2 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
shape = _geo.Rect.from_rect(rect=nwmbb1, top=nwmbb2.top)
layouter.add_wire(net=dvdd, wire=nwm, shape=shape)
shape = _geo.Rect.from_rect(rect=lipindvssbb1, top=lipindvssbb2.top)
layouter.add_wire(net=dvss, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvss, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvss_leftrowm1bb = lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=lipindvddbb1, top=lipindvddbb2.top)
layouter.add_wire(net=dvdd, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvdd, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvdd_leftrowm1bb = lay.bounds(mask=m1.mask)
# right
rot_rightrow = _geo.Rotation.R90
_tie_rotbb = rot_rightrow*_tie_bb
x_rightrow = _frm.boundary.right - dbound - _tie_rotbb.right
inst = ckt.instantiate(tie_cell, name="lrtie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
y_tie = dbound - _tie_rotbb.bottom
lay = layouter.place(inst, x=x_rightrow, y=y_tie, rotation=rot_rightrow)
nwmbb1 = lay.bounds(mask=nwm.mask)
lipindvssbb1 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb1 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
inst = ckt.instantiate(tie_cell, name="urtie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
y_tie = _frm.boundary.top - dbound - _tie_rotbb.top
lay = layouter.place(inst, x=x_rightrow, y=y_tie, rotation=rot_rightrow)
nwmbb2 = lay.bounds(mask=nwm.mask)
lipindvssbb2 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb2 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
shape = _geo.Rect.from_rect(rect=nwmbb1, top=nwmbb2.top)
layouter.add_wire(net=dvdd, wire=nwm, shape=shape)
shape = _geo.Rect.from_rect(rect=lipindvssbb1, top=lipindvssbb2.top)
layouter.add_wire(net=dvss, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvss, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvss_rightrowm1bb = lay.bounds(mask=m1.mask)
assert dvss_rightrowm1bb.center.x < dvss_bb.center.x, "Internal error"
w = tech.on_grid(dvss_rightrowm1bb.width, mult=2, rounding="floor")
h = tech.on_grid(dvss_bb.height, mult=2, rounding="floor")
x_via = tech.on_grid(dvss_rightrowm1bb.center.x)
y_via = tech.on_grid(dvss_bb.center.y)
layouter.add_wire(
net=dvss, wire=via, x=x_via, y=y_via,
bottom_width=w, bottom_height=h, top_width=w, top_height=h,
)
via2_lay = layouter.add_wire(
net=dvss, wire=via2, x=x_via, y=y_via,
bottom_width=w, bottom_height=h, top_width=w, top_height=h,
)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
shape = _geo.Rect.from_rect(rect=dvss_bb, left=via2_m3bb.left)
layouter.add_wire(net=dvss, wire=m3, shape=shape)
layouter.add_wire(net=dvss, wire=m3, pin=m3pin, shape=dvss_bb)
shape = _geo.Rect.from_rect(rect=lipindvddbb1, top=lipindvddbb2.top)
layouter.add_wire(net=dvdd, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvdd, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvdd_rightrowm1bb = lay.bounds(mask=m1.mask)
assert dvdd_rightrowm1bb.center.x < dvdd_bb.center.x, "Internal error"
w = tech.on_grid(dvdd_rightrowm1bb.width, mult=2, rounding="floor")
h = tech.on_grid(dvdd_bb.height, mult=2, rounding="floor")
x_via = tech.on_grid(dvdd_rightrowm1bb.center.x)
y_via = tech.on_grid(dvdd_bb.center.y)
layouter.add_wire(
net=dvdd, wire=via, x=x_via, y=y_via,
bottom_width=w, bottom_height=h, top_width=w, top_height=h,
)
via2_lay = layouter.add_wire(
net=dvdd, wire=via2, x=x_via, y=y_via,
bottom_width=w, bottom_height=h, top_width=w, top_height=h,
)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
shape = _geo.Rect.from_rect(rect=dvdd_bb, left=via2_m3bb.left)
lay = layouter.add_wire(net=dvdd, wire=m3, shape=shape)
layouter.add_wire(net=dvdd, wire=m3, pin=m3pin, shape=dvdd_bb)
# below SRAM
rot_midrow = _geo.Rotation.R0
_tie_rotbb = rot_midrow*_tie_bb
y_midrow = sram_bb.bottom - dbound - _tie_rotbb.top
inst = ckt.instantiate(tie_cell, name="mltie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
x_tie = sram_bb.left - _tie_rotbb.left
lay = layouter.place(inst, x=x_tie, y=y_midrow, rotation=rot_midrow)
nwmbb1 = lay.bounds(mask=nwm.mask)
lipindvssbb1 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb1 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
inst = ckt.instantiate(tie_cell, name="mrtie")
dvss.childports += inst.ports.vss
dvdd.childports += inst.ports.vdd
x_tie = sram_bb.right - _tie_rotbb.right
lay = layouter.place(inst, x=x_tie, y=y_midrow, rotation=rot_midrow)
nwmbb2 = lay.bounds(mask=nwm.mask)
lipindvssbb2 = lay.bounds(mask=lipin.mask, net=dvss, depth=1)
lipindvddbb2 = lay.bounds(mask=lipin.mask, net=dvdd, depth=1)
shape = _geo.Rect.from_rect(rect=nwmbb1, right=nwmbb2.right)
layouter.add_wire(net=dvdd, wire=nwm, shape=shape)
shape = _geo.Rect.from_rect(rect=lipindvssbb1, right=lipindvssbb2.right)
layouter.add_wire(net=dvss, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvss, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvss_midrowm1bb = lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=lipindvddbb1, right=lipindvddbb2.right)
layouter.add_wire(net=dvdd, wire=li, shape=shape)
w = tech.on_grid(shape.width, mult=2, rounding="floor")
h = tech.on_grid(shape.height, mult=2, rounding="floor")
o = tech.on_grid(shape.center)
lay = layouter.add_wire(
net=dvdd, wire=mcon, origin=o, bottom_width=w, bottom_height=h,
)
dvdd_midrowm1bb = lay.bounds(mask=m1.mask)
assert dvss_leftrowm1bb.center.x > dvdd_leftrowm1bb.center.x, "Internal error"
assert dvss_rightrowm1bb.center.x > dvdd_rightrowm1bb.center.x, "Internal error"
shape = _geo.Rect.from_rect(
rect=dvss_midrowm1bb,
left=dvss_leftrowm1bb.left, right=dvdd_rightrowm1bb.left - 1.0,
)
layouter.add_wire(net=dvss, wire=m1, shape=shape)
w = shape.height
_via_lay = layouter.wire_layout(
net=dvss, wire=via, bottom_width=w, bottom_height=w,
)
_via_m1bb = _via_lay.bounds(mask=m1.mask)
y_via = shape.center.y
x_via = shape.right - _via_m1bb.right
lay = layouter.place(_via_lay, x=x_via, y=y_via)
m2bb1 = lay.bounds(mask=m2.mask)
x_via = dvss_rightrowm1bb.center.x
lay = layouter.place(_via_lay, x=x_via, y=y_via)
m2bb2 = lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=m2bb1, right=m2bb2.right)
layouter.add_wire(net=dvss, wire=m2, shape=shape)
shape = _geo.Rect.from_rect(
rect=dvdd_midrowm1bb,
left=(dvss_leftrowm1bb.right + 1.0), right=dvdd_rightrowm1bb.right,
)
layouter.add_wire(net=dvdd, wire=m1, shape=shape)
w = shape.height
_via_lay = layouter.wire_layout(
net=dvdd, wire=via, bottom_width=w, bottom_height=w,
)
_via_m1bb = _via_lay.bounds(mask=m1.mask)
y_via = shape.center.y
x_via = shape.left - _via_m1bb.left
lay = layouter.place(_via_lay, x=x_via, y=y_via)
m2bb1 = lay.bounds(mask=m2.mask)
x_via = dvdd_leftrowm1bb.center.x
lay = layouter.place(_via_lay, x=x_via, y=y_via)
m2bb2 = lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=m2bb1, left=m2bb2.left)
layouter.add_wire(net=dvdd, wire=m2, shape=shape)
# Connect the SRAM signals
#
# vss
spec = io_sig2spec["vss"]
sram_port = sram.ports[spec.sram_signal]
net = ckt.new_net(name=spec.toppin_name, external=True, childports=sram_port)
toppin_bb = _frm.toppins[spec.toppin_name]
bbs = tuple(ms.shape.bounds for ms in sram_lay.filter_polygons(
net=net, mask=m2pin.mask, depth=1, split=True,
))
left = max(bb.left for bb in bbs)
top = max(bb.top for bb in bbs)
bottom = min(bb.bottom for bb in bbs)
w = 10.0
x_via2 = left + w/2.0
right = left + w
for bb in bbs:
y_via2 = bb.center.y
layouter.add_wire(
net=net, wire=via2, x=x_via2, y=y_via2,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
shape = _geo.Rect(
left=left, bottom=bottom, right=right, top=(top + 20.0),
)
layouter.add_wire(net=net, wire=m3, shape=shape)
shape = _geo.Rect(
left=toppin_bb.left, bottom=(top + 10.0), right=right, top=(top+20.0),
)
layouter.add_wire(net=net, wire=m3, shape=shape)
shape = _geo.Rect.from_rect(rect=toppin_bb, bottom=(top + 10.0))
layouter.add_wire(net=net, wire=m3, shape=shape)
layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=toppin_bb)
# io_clamp_low
clamp_bb = _frm.toppins["io_clamp_low[1]"]
shape = _geo.Rect.from_rect(rect=clamp_bb, bottom=(top + 10))
layouter.add_wire(net=net, wire=m3, shape=shape)
# vdd
spec = io_sig2spec["vdd"]
sram_port = sram.ports[spec.sram_signal]
net = ckt.new_net(name=spec.toppin_name, external=True, childports=sram_port)
toppin_bb = _frm.toppins[spec.toppin_name]
bbs = tuple(ms.shape.bounds for ms in sram_lay.filter_polygons(
net=net, mask=m2pin.mask, depth=1, split=True,
))
right = min(bb.right for bb in bbs)
top = max(bb.top for bb in bbs)
bottom = min(bb.bottom for bb in bbs)
w = 10.0
x_via2 = right - w/2.0
left = right - w
for bb in bbs:
y_via2 = bb.center.y
layouter.add_wire(
net=net, wire=via2, x=x_via2, y=y_via2,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
shape = _geo.Rect(
left=left, bottom=bottom, right=right, top=(top + 20.0),
)
layouter.add_wire(net=net, wire=m3, shape=shape)
shape = _geo.Rect(
left=left, bottom=(top + 10.0), right=toppin_bb.right, top=(top + 20.0),
)
layouter.add_wire(net=net, wire=m3, shape=shape)
shape = _geo.Rect.from_rect(rect=toppin_bb, bottom=(top + 10.0))
layouter.add_wire(net=net, wire=m3, shape=shape)
layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=toppin_bb)
# io_clamp_high
clamp_bb = _frm.toppins["io_clamp_high[1]"]
w = clamp_bb.width
shape = _geo.Rect.from_rect(rect=clamp_bb, bottom=(clamp_bb.bottom - 2*w))
layouter.add_wire(net=net, wire=m3, shape=shape)
shape = _geo.Rect(
left=clamp_bb.left, bottom=(clamp_bb.bottom - 2*w),
right=toppin_bb.right, top=(clamp_bb.bottom - w)
)
layouter.add_wire(net=net, wire=m3, shape=shape)
# connect the input signals
a_col = 0
for sig_name in (
*(f"a[{a_bit}]" for a_bit in reversed(range(self.a_bits))), # Reversed for a_col
"clk", "we[0]",
*(f"d[{bit}]" for bit in range(self.word_size)),
):
spec = io_sig2spec[sig_name]
prefix = spec.prefix
num = spec.io_number
pin_name = spec.toppin_name
oeb_name = f"io_oeb[{num}]"
out_name = f"io_out[{num}]"
assert spec.io_type == "io_in", "Internal error"
assert spec.oeb, "Internal error"
sram_port = sram.ports[sig_name]
# instantiate cells
buf = ckt.instantiate(buf_cell, name="{prefix}buf")
one = ckt.instantiate(one_cell, name=f"{prefix}one")
zero = ckt.instantiate(zero_cell, name=f"{prefix}zero")
tie = ckt.instantiate(tie_cell, name=f"{prefix}tie")
# create nets
pin_net = ckt.new_net(name=pin_name, external=True, childports=buf.ports.i)
sig_net = ckt.new_net(name=sig_name, external=False, childports=(
buf.ports.q, sram_port,
))
oeb_net = ckt.new_net(
name=oeb_name, external=True, childports=one.ports.one,
)
out_net = ckt.new_net(
name=out_name, external=True, childports=zero.ports.zero,
)
# get bbs; SRAM m2 pin to m3 pin
toppin_bb = _frm.toppins[pin_name]
oeb_bb = _frm.toppins[oeb_name]
out_bb = _frm.toppins[out_name]
if sig_name.startswith("a["):
# Connect signal up to m2
sram_m1pinbb = sram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1)
w = 10.0
right = sram_bb.left - (2*a_col + 1)*w
left = right - 1
a_col += 1
x_via = right - w/2.0
y_via = tech.on_grid(sram_m1pinbb.center.y)
via_lay = layouter.add_wire(
net=sig_net, wire=via, x=x_via, y=y_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via_m1bb = via_lay.bounds(mask=m1.mask)
sram_m2pinbb = via_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sram_m1pinbb, left=via_m1bb.left)
layouter.add_wire(net=sig_net, wire=m1, shape=shape)
else:
sram_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=sig_net, depth=1)
is_leftrow = toppin_bb.center.x < sram_m2pinbb.center.x
rot = rot_leftrow if is_leftrow else rot_rightrow
x_row = x_leftrow if is_leftrow else x_rightrow
# place buf
_buf_rotilipinbb = rot*_buf_ilipinbb
y_buf = tech.on_grid(
toppin_bb.top - _buf_rotilipinbb.bottom,
mult=2,
)
buf_lay = layouter.place(buf, x=x_row, y=y_buf, rotation=rot)
pinbuf_lipinbb = buf_lay.bounds(mask=lipin.mask, net=pin_net, depth=1)
sigbuf_lipinbb = buf_lay.bounds(mask=lipin.mask, net=sig_net, depth=1)
buf_bb = buf_lay.boundary
assert buf_bb is not None
# place tie cell
_tie_rotbb = rot*_tie_bb
if is_leftrow:
y_tie = buf_bb.top - _tie_rotbb.bottom
else:
y_tie = buf_bb.bottom - _tie_rotbb.top
layouter.place(tie, x=x_row, y=y_tie, rotation=rot)
# place zero cell
_zero_rotbb = rot*_zero_bb
if is_leftrow:
y_zero = buf_bb.bottom - _zero_rotbb.top
else:
y_zero = buf_bb.top - _zero_rotbb.bottom
zero_lay = layouter.place(zero, x=x_row, y=y_zero, rotation=rot)
zero_bb = zero_lay.boundary
assert zero_bb is not None
zeroout_lipinbb = zero_lay.bounds(mask=lipin.mask, net=out_net, depth=1)
# place one cell
_one_rotbb = rot*_one_bb
if is_leftrow:
y_one = zero_bb.bottom - _one_rotbb.top
else:
y_one = zero_bb.top - _one_rotbb.bottom
one_lay = layouter.place(one, x=x_row, y=y_one, rotation=rot)
oneoeb_lipinbb = one_lay.bounds(mask=lipin.mask, net=oeb_net, depth=1)
# connect pin_net
w = tech.on_grid(pinbuf_lipinbb.width, mult=2, rounding="floor")
o_via = tech.on_grid(pinbuf_lipinbb.center)
layouter.add_wire(
net=pin_net, wire=mcon, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
layouter.add_wire(
net=pin_net, wire=via, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_lay = layouter.add_wire(
net=pin_net, wire=via2, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=toppin_bb, right=via2_m3bb.right)
else:
shape = _geo.Rect.from_rect(rect=toppin_bb, left=via2_m3bb.left)
layouter.add_wire(net=pin_net, wire=m3, shape=shape)
layouter.add_wire(net=pin_net, wire=m3, pin=m3pin, shape=toppin_bb)
# connect sig_net
w = tech.on_grid(sigbuf_lipinbb.width, mult=2, rounding="floor")
o_via = tech.on_grid(sigbuf_lipinbb.center)
layouter.add_wire(
net=sig_net, wire=mcon, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
layouter.add_wire(
net=sig_net, wire=via, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_lay = layouter.add_wire(
net=sig_net, wire=via2, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_m3bb1 = via2_lay.bounds(mask=m3.mask)
_via2_lay = layouter.wire_layout(
net=sig_net, wire=via2, rows=6, columns=3,
)
_via2_m2bb = _via2_lay.bounds(mask=m2.mask)
if is_leftrow:
x_via2 = sram_m2pinbb.right - _via2_m2bb.right
else:
x_via2 = sram_m2pinbb.left - _via2_m2bb.left
y_via2 = via2_m3bb1.center.y
via2_lay = layouter.place(_via2_lay, x=x_via2, y=y_via2)
via2_m2bb = via2_lay.bounds(mask=m2.mask)
via2_m3bb2 = via2_lay.bounds(mask=m3.mask)
shape = _geo.Rect.from_rect(rect=sram_m2pinbb, bottom=via2_m2bb.bottom)
layouter.add_wire(net=sig_net, wire=m2, shape=shape)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=via2_m3bb1, right=via2_m3bb2.right)
else:
shape = _geo.Rect.from_rect(rect=via2_m3bb1, left=via2_m3bb2.left)
layouter.add_wire(net=sig_net, wire=m3, shape=shape)
# connect oeb_net
w = tech.on_grid(oneoeb_lipinbb.width, mult=2, rounding="floor")
o_via = tech.on_grid(oneoeb_lipinbb.center)
layouter.add_wire(
net=oeb_net, wire=mcon, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via_lay = layouter.add_wire(
net=oeb_net, wire=via, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via_m2bb = via_lay.bounds(mask=m2.mask)
o_via2 = _geo.Point.from_point(point=o_via, y=oeb_bb.center.y)
via2_lay = layouter.add_wire(
net=oeb_net, wire=via2, origin=o_via2,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_m2bb = via2_lay.bounds(mask=m2.mask)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=via_m2bb, bottom=via2_m2bb.bottom)
else:
shape = _geo.Rect.from_rect(rect=via_m2bb, top=via2_m2bb.top)
layouter.add_wire(net=oeb_net, wire=m2, shape=shape)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=oeb_bb, right=via2_m3bb.right)
else:
shape = _geo.Rect.from_rect(rect=oeb_bb, left=via2_m3bb.left)
layouter.add_wire(net=oeb_net, wire=m3, shape=shape)
layouter.add_wire(net=oeb_net, wire=m3, pin=m3pin, shape=oeb_bb)
# connect out_net
w = tech.on_grid(zeroout_lipinbb.width, mult=2, rounding="floor")
o_via = tech.on_grid(zeroout_lipinbb.center)
layouter.add_wire(
net=out_net, wire=mcon, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
layouter.add_wire(
net=out_net, wire=via, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_lay = layouter.add_wire(
net=out_net, wire=via2, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=via2_m3bb, bottom=out_bb.bottom)
else:
shape = _geo.Rect.from_rect(rect=via2_m3bb, top=out_bb.top)
layouter.add_wire(net=out_net, wire=m3, shape=shape)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=out_bb, right=via2_m3bb.right)
else:
shape = _geo.Rect.from_rect(rect=out_bb, left=via2_m3bb.left)
layouter.add_wire(net=out_net, wire=m3, shape=shape)
layouter.add_wire(net=out_net, wire=m3, pin=m3pin, shape=out_bb)
# connect the output signals
for sig_name in (
*(f"q[{bit}]" for bit in range(self.word_size)),
):
spec = io_sig2spec[sig_name]
prefix = spec.prefix
num = spec.io_number
pin_name = spec.toppin_name
oeb_name = f"io_oeb[{num}]"
assert spec.io_type == "io_out", "Internal error"
assert not spec.oeb, "Internal error"
sram_port = sram.ports[spec.sram_signal]
# instantiate cells
buf = ckt.instantiate(buf_cell, name="{prefix}buf")
zero = ckt.instantiate(zero_cell, name=f"{prefix}zero")
tie = ckt.instantiate(tie_cell, name=f"{prefix}tie")
# create nets
pin_net = ckt.new_net(name=pin_name, external=True, childports=buf.ports.q)
sig_net = ckt.new_net(name=sig_name, external=False, childports=(
buf.ports.i, sram_port,
))
oeb_net = ckt.new_net(
name=oeb_name, external=True, childports=zero.ports.zero,
)
# get bss
toppin_bb = _frm.toppins[pin_name]
oeb_bb = _frm.toppins[oeb_name]
sram_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=sig_net, depth=1)
is_leftrow = toppin_bb.center.x < sram_m2pinbb.center.x
# place buf
rot = rot_midrow
y_row = y_midrow
_buf_rotilipinbb = rot*_buf_ilipinbb
x_buf = tech.on_grid(
sram_m2pinbb.right - _buf_rotilipinbb.left,
mult=2, rounding="ceiling",
)
buf_lay = layouter.place(buf, x=x_buf, y=y_row, rotation=rot)
pinbuf_lipinbb = buf_lay.bounds(mask=lipin.mask, net=pin_net, depth=1)
sigbuf_lipinbb = buf_lay.bounds(mask=lipin.mask, net=sig_net, depth=1)
buf_bb = buf_lay.boundary
assert buf_bb is not None
# place zero
rot = rot_leftrow if is_leftrow else rot_rightrow
x_row = x_leftrow if is_leftrow else x_rightrow
_zero_rotzerolipinbb = rot*_zero_zerolipinbb
y_zero = oeb_bb.center.y - _zero_rotzerolipinbb.center.y
zero_lay = layouter.place(zero, x=x_row, y=y_zero, rotation=rot)
zero_zerolipinbb = zero_lay.bounds(mask=lipin.mask, net=oeb_net, depth=1)
zero_bb = zero_lay.boundary
assert zero_bb is not None
# place tie
_tie_rotbb = rot*_tie_bb
y_tie = zero_bb.top - _tie_rotbb.bottom
layouter.place(tie, x=x_row, y=y_tie, rotation=rot)
# connect sig_net
h = tech.on_grid(sigbuf_lipinbb.height, mult=2, rounding="floor")
o_via = tech.on_grid(sigbuf_lipinbb.center)
layouter.add_wire(
net=sig_net, wire=mcon, origin=o_via,
bottom_height=h, bottom_enclosure="tall",
top_height=h, top_enclosure="tall",
)
via_lay = layouter.add_wire(
net=sig_net, wire=via, origin=o_via,
bottom_height=h, bottom_enclosure="tall",
top_height=h, top_enclosure="tall",
)
via_m2bb = via_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sram_m2pinbb, bottom=via_m2bb.bottom)
layouter.add_wire(net=sig_net, wire=m2, shape=shape)
# connect pin_net
h = tech.on_grid(pinbuf_lipinbb.height, mult=2, rounding="floor")
o_via = tech.on_grid(pinbuf_lipinbb.center)
layouter.add_wire(
net=sig_net, wire=mcon, origin=o_via,
bottom_height=h, bottom_enclosure="tall",
top_height=h, top_enclosure="tall",
)
via_lay = layouter.add_wire(
net=sig_net, wire=via, origin=o_via,
bottom_height=h, bottom_enclosure="tall",
top_height=h, top_enclosure="tall",
)
via_m2bb = via_lay.bounds(mask=m2.mask)
_via2_lay = layouter.wire_layout(
net=pin_net, wire=via2, rows=6, columns=3,
)
_via2_m2bb = _via2_lay.bounds(mask=m2.mask)
if is_leftrow:
x_via2 = via_m2bb.right - _via2_m2bb.right
else:
x_via2 = via_m2bb.left - _via2_m2bb.left
y_via2 = toppin_bb.center.y
via2_lay = layouter.place(_via2_lay, x=x_via2, y=y_via2)
via2_m2bb = via2_lay.bounds(mask=m2.mask)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
shape = _geo.Rect.from_rect(rect=via_m2bb, bottom=via2_m2bb.bottom)
layouter.add_wire(net=pin_net, wire=m2, shape=shape)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=toppin_bb, right=via2_m3bb.right)
else:
shape = _geo.Rect.from_rect(rect=toppin_bb, left=via2_m3bb.left)
layouter.add_wire(net=pin_net, wire=m3, shape=shape)
layouter.add_wire(net=pin_net, wire=m3, pin=m3pin, shape=toppin_bb)
# connect oeb_net
w = tech.on_grid(zero_zerolipinbb.width, mult=2, rounding="floor")
o_via = tech.on_grid(zero_zerolipinbb.center)
layouter.add_wire(
net=oeb_net, wire=mcon, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
layouter.add_wire(
net=oeb_net, wire=via, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_lay = layouter.add_wire(
net=oeb_net, wire=via2, origin=o_via,
bottom_width=w, bottom_enclosure="wide",
top_width=w, top_enclosure="wide",
)
via2_m3bb = via2_lay.bounds(mask=m3.mask)
if is_leftrow:
shape = _geo.Rect.from_rect(rect=oeb_bb, right=via2_m3bb.right)
else:
shape = _geo.Rect.from_rect(rect=oeb_bb, left=via2_m3bb.left)
layouter.add_wire(net=oeb_net, wire=m3, shape=shape)
layouter.add_wire(net=oeb_net, wire=m3, pin=m3pin, shape=oeb_bb)
# boundary
#
layout.boundary = _frm.boundary