blob: cfde4a49c9f67121bd9a7a2ca9f791f9b2a2213b [file] [log] [blame]
# SPDX-License-Identifier: GPL-2.0-or-later
from typing import Optional, List, Tuple, Union, cast
from pandas import notna
from pdkmaster.technology import geometry as _geo, primitive as _prm
from pdkmaster.design import circuit as _ckt, library as _lbry
from c4m import flexmem as _mem
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 SPCharacterizationWrapper(_lbry._OnDemandCell[_lbry.Library]):
def __init__(self, *,
lib: _lbry.Library, spcell: _mem.sp6t.factory._SP6TBlock,
):
name = f"Char{spcell.name}"
super().__init__(lib=lib, name=name)
self.spcell = spcell
def _create_circuit(self):
stdcells = sky130.stdcelllib.cells
ff_cell = stdcells.sff1_x4
mux_cell = stdcells.mx2_x2
spcell = self.spcell
self.a_bits = a_bits = spcell.a_bits
self.word_size = word_size = spcell.word_size
self.we_size = we_size = spcell.we_size
assert word_size%we_size == 0
self.byte_size = byte_size = word_size//we_size
ckt = self.new_circuit()
# Registers for shifting and capturing the inputs for the sram
# Order is aligned with the layout
_in_shiftreg: List[_ckt._CellInstance] = []
_in_capturereg: List[_ckt._CellInstance] = []
for n_a in range(a_bits):
_in_shiftreg.append(ckt.instantiate(ff_cell, name=f"aff_s[{n_a}]"))
_in_capturereg.append(ckt.instantiate(ff_cell, name=f"aff_c[{n_a}]"))
for n_we in range(we_size):
_in_shiftreg.append(ckt.instantiate(ff_cell, name=f"weff_s[{n_we}]"))
_in_capturereg.append(ckt.instantiate(ff_cell, name=f"weff_c[{n_we}]"))
for n_byte in range(byte_size):
n_bit = n_we*byte_size + n_byte
_in_shiftreg.append(ckt.instantiate(ff_cell, name=f"dff_s[{n_bit}]"))
_in_capturereg.append(ckt.instantiate(ff_cell, name=f"dff_c[{n_bit}]"))
in_shiftreg = tuple(_in_shiftreg)
in_capturereg = tuple(_in_capturereg)
in_all = (*in_shiftreg, *in_capturereg)
# Register for capturing the outputs
_out_shiftreg: List[_ckt._CellInstance] = []
_out_capturereg: List[_ckt._CellInstance] = []
_out_muxes: List[_ckt._CellInstance] = []
for n_bit in range(word_size):
_out_shiftreg.append(ckt.instantiate(ff_cell, name=f"qff_s[{n_bit}]"))
_out_capturereg.append(ckt.instantiate(ff_cell, name=f"qff_c[{n_bit}]"))
_out_muxes.append(ckt.instantiate(mux_cell, name=f"qmux[{n_bit}]"))
out_shiftreg = tuple(_out_shiftreg)
out_capturereg = tuple(_out_capturereg)
out_muxes = tuple(_out_muxes)
out_all = (*out_shiftreg, *out_capturereg, *out_muxes)
in_captclkff = ckt.instantiate(ff_cell, name="incaptclkff")
out_captclkff = ckt.instantiate(ff_cell, name="outcaptclkff")
sram = ckt.instantiate(spcell, name="sram")
ckt.new_net(name="dvss", external=True, childports=(
inst.ports.vss for inst in (*in_all, *out_all, in_captclkff, out_captclkff)
))
ckt.new_net(name="dvdd", external=True, childports=(
inst.ports.vdd for inst in (*in_all, *out_all, in_captclkff, out_captclkff)
))
ckt.new_net(name="svss", external=True, childports=sram.ports.vss)
ckt.new_net(name="svdd", external=True, childports=sram.ports.vdd)
ckt.new_net(name="shift_in", external=True, childports=in_all[0].ports.i)
ckt.new_net(name="shift_out", external=True, childports=out_shiftreg[-1].ports.q)
ckt.new_net(name="sramclk", external=True, childports=(
sram.ports.clk, in_captclkff.ports.ck, out_captclkff.ports.ck,
))
ckt.new_net(name="in_shiftclk", external=True, childports=(
ff.ports.ck for ff in in_shiftreg
))
ckt.new_net(name="in_captureclk", external=True, childports=(
*(ff.ports.ck for ff in in_capturereg), in_captclkff.ports.i,
))
ckt.new_net(name="in_captureclk_l", external=True, childports=(
in_captclkff.ports.q
))
ckt.new_net(name="out_shiftclk", external=True, childports=(
ff.ports.ck for ff in out_shiftreg
))
ckt.new_net(name="out_captureclk", external=True, childports=(
*(ff.ports.ck for ff in out_capturereg), out_captclkff.ports.i,
))
ckt.new_net(name="out_captureclk_l", external=True, childports=(
out_captclkff.ports.q
))
ckt.new_net(name="out_docapture", external=True, childports=(
mux.ports.cmd for mux in out_muxes
))
# Connect the input shift registers
# Output of flop in shift reg is connected to input of corresponding flop in
# the capture reg and to the input of next flop in shift reg. Last flop
# connected to the input
# Output of flop in capture reg is connected to corresponding sram input pin
n = 0
for n_a in range(a_bits):
ckt.new_net(name=f"a_s[{n_a}]", external=False, childports=(
in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
in_shiftreg[n + 1].ports.i
))
ckt.new_net(name=f"a_c[{n_a}]", external=False, childports=(
in_capturereg[n].ports.q, sram.ports[f"a[{n_a}]"],
))
n += 1
for n_we in range(we_size):
ckt.new_net(name=f"we_s[{n_we}]", external=False, childports=(
in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
in_shiftreg[n + 1].ports.i
))
ckt.new_net(name=f"we_c[{n_we}]", external=False, childports=(
in_capturereg[n].ports.q, sram.ports[f"we[{n_we}]"],
))
n += 1
for n_byte in range(byte_size):
n_bit = n_we*byte_size + n_byte
nextgatepin = (
in_shiftreg[n + 1].ports.i
if n < (len(in_shiftreg) - 1)
else out_muxes[0].ports.i1
)
ckt.new_net(name=f"d_s[{n_bit}]", external=False, childports=(
in_shiftreg[n].ports.q, in_capturereg[n].ports.i,
nextgatepin,
))
ckt.new_net(name=f"d_c[{n_bit}]", external=False, childports=(
in_capturereg[n].ports.q, sram.ports[f"d[{n_bit}]"],
))
n += 1
# Connect the output shift registers
# The output of the sram is connected to the input of the corresponding flop
# in the capture reg. The output of the flop in the capture reg is connect
# to input i0 of the corresponding mux and the output of the previous flop in
# shift reg is connect to i1 of the mux.
for n_bit in range(word_size):
ckt.new_net(name=f"q_c[{n_bit}]", external=False, childports=(
sram.ports[f"q[{n_bit}]"], out_capturereg[n_bit].ports.i,
))
ckt.new_net(name=f"q_cl[{n_bit}]", external=False, childports=(
out_capturereg[n_bit].ports.q, out_muxes[n_bit].ports.i0,
))
if n_bit < (word_size - 1):
ckt.new_net(name=f"q_s[{n_bit}]", external=False, childports=(
out_shiftreg[n_bit].ports.q, out_muxes[n_bit + 1].ports.i1,
))
ckt.new_net(name=f"q_mux[{n_bit}]", external=False, childports=(
out_muxes[n_bit].ports.q, out_shiftreg[n_bit].ports.i,
))
def _create_layout(self):
tech = self.tech
prims = tech.primitives
ckt = self.circuit
nets = ckt.nets
insts = ckt.instances
nwm = cast(_prm.Well, prims.nwm)
li = cast(_prm.MetalWire, prims.li)
assert li.pin is not None
lipin = li.pin[0]
mcon = cast(_prm.Via, prims.mcon)
m1 = cast(_prm.MetalWire, prims.m1)
assert m1.pin is not None
m1pin = m1.pin[0]
via = cast(_prm.Via, prims.via)
m2 = cast(_prm.MetalWire, prims.m2)
assert m2.pin is not None
m2pin = m2.pin[0]
# Place the SRAM block
layouter = self.new_circuitlayouter()
sram_lay = layouter.place(insts.sram, origin=_geo.origin)
sram_bb = sram_lay.boundary
assert sram_bb is not None
sramclk_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=nets.sramclk, depth=1)
# Place and connect a flipflops
x_s = y_s0 = None
x_c = y_c0 = None
dy = None
net_s_prev = None
mconnets_m1bb1_prev = None
mconacclk_m1bb_last = None
mconasclk_m1bb_last = None
affcvss_lipinbb_last = None
affcvdd_lipinbb_last = None
affsvss_lipinbb_last = None
affsvdd_lipinbb_last = None
for n_a in range(self.a_bits):
net_s = nets[f"a_s[{n_a}]"]
net_c = nets[f"a_c[{n_a}]"]
_affs_lay = layouter.inst_layout(
inst=insts[f"aff_s[{n_a}]"], rotation=_geo.Rotation.MX90,
)
_affc_lay = layouter.inst_layout(
inst=insts[f"aff_c[{n_a}]"], rotation=_geo.Rotation.R90,
)
sramnetc_m1pinbb = sram_lay.bounds(mask=m1pin.mask, net=net_c, depth=1)
if n_a == 0:
_affs_bb = _affs_lay.boundary
assert _affs_bb is not None
_affc_bb = _affc_lay.boundary
assert _affc_bb is not None
# Compute placement based on first flops
x_c = -3.0 - _affc_bb.right
y_c0 = sramnetc_m1pinbb.bottom - _affc_bb.top
x_s = x_c + _affc_bb.left - _affs_bb.right
y_s0 = sramnetc_m1pinbb.bottom - _affs_bb.top
dy = _affs_bb.height
assert x_s is not None
assert y_s0 is not None
assert x_c is not None
assert y_c0 is not None
assert dy is not None
# Place flops for a part of registers
y_s = y_s0 + n_a*dy
y_c = y_c0 + n_a*dy
affs_lay = layouter.place(_affs_lay, x=x_s, y=y_s)
affsnets_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net_s)
affssclk_lipinbb = affs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
affsvss_lipinbb_last = affs_lay.bounds(mask=lipin.mask, net=nets.dvss)
affsvdd_lipinbb_last = affs_lay.bounds(mask=lipin.mask, net=nets.dvdd)
affc_lay = layouter.place(_affc_lay, x=x_c, y=y_c)
affcnets_lipinbb = affc_lay.bounds(mask=lipin.mask, net=net_s)
affcnetc_lipinbb = affc_lay.bounds(mask=lipin.mask, net=net_c)
affccclk_lipinbb = affc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
affcvss_lipinbb_last = affc_lay.bounds(mask=lipin.mask, net=nets.dvss)
affcvdd_lipinbb_last = affc_lay.bounds(mask=lipin.mask, net=nets.dvdd)
# in_shiftclk
mconsclk_lay = layouter.add_wire(
net=nets.in_shiftclk, wire=mcon, columns=2,
origin=affssclk_lipinbb.center,
)
mconasclk_m1bb_last = mconsclk_lay.bounds(mask=m1.mask)
# in_captureclk
mconcclk_lay = layouter.add_wire(
net=nets.in_captureclk, wire=mcon, columns=2,
origin=affccclk_lipinbb.center,
)
mconacclk_m1bb_last = mconcclk_lay.bounds(mask=m1.mask)
# net_s
_mconnets_lay = layouter.wire_layout(
net=net_s, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconnets_libb = _mconnets_lay.bounds(mask=li.mask)
x = affsnets_lipinbb.right - _mconnets_libb.right
y = affsnets_lipinbb.center.y
mconnets_lay1 = layouter.place(_mconnets_lay, x=x, y=y)
mconnets_m1bb1 = mconnets_lay1.bounds(mask=m1.mask)
x = affcnets_lipinbb.left - _mconnets_libb.left
y = affcnets_lipinbb.center.y
mconnets_lay2 = layouter.place(_mconnets_lay, x=x, y=y)
mconnets_m1bb2 = mconnets_lay2.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=mconnets_m1bb2, top=mconnets_m1bb1.top)
layouter.add_wire(net=net_s, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconnets_m1bb1, right=mconnets_m1bb2.right)
layouter.add_wire(net=net_s, wire=m1, shape=shape)
_mconnetc_lay = layouter.wire_layout(
net=net_c, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconnetc_libb = _mconnetc_lay.bounds(mask=li.mask)
x = affcnetc_lipinbb.right - _mconnetc_libb.right
y = affcnetc_lipinbb.center.y
mconnetc_lay = layouter.place(_mconnetc_lay, x=x, y=y)
mconnetc_m1bb = mconnetc_lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=sramnetc_m1pinbb, left = mconnetc_m1bb.left)
layouter.add_wire(net=net_c, wire=m1, shape=shape)
if sramnetc_m1pinbb.top > mconnetc_m1bb.top:
shape = _geo.Rect.from_rect(rect=mconnetc_m1bb, top=sramnetc_m1pinbb.top)
else:
assert sramnetc_m1pinbb.bottom < mconnetc_m1bb.bottom
shape = _geo.Rect.from_rect(
rect=mconnetc_m1bb, bottom=sramnetc_m1pinbb.bottom,
)
layouter.add_wire(net=net_c, wire=m1, shape=shape)
# net_s_prev
if net_s_prev is not None:
affsnetsp_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net_s_prev)
assert mconnets_m1bb1_prev is not None
_mconnetsp_lay = layouter.wire_layout(
net=net_s_prev, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconnetsp_libb = _mconnets_lay.bounds(mask=li.mask)
x = affsnetsp_lipinbb.right - _mconnetsp_libb.right
y = affsnetsp_lipinbb.center.y
mconnetsp_lay = layouter.place(_mconnetsp_lay, x=x, y=y)
mconnetsp_m1bb = mconnetsp_lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(
rect=mconnets_m1bb1_prev, top=mconnetsp_m1bb.top,
)
layouter.add_wire(net=net_s_prev, wire=m1, shape=shape)
else:
assert n_a == 0
net = nets.shift_in
affssin_lipinbb = affs_lay.bounds(mask=lipin.mask, net=net)
_mconsin_lay = layouter.wire_layout(
net=net, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconsin_libb = _mconsin_lay.bounds(mask=li.mask)
x = affssin_lipinbb.left - _mconsin_libb.left
y = affssin_lipinbb.center.y
mconsin_lay = layouter.place(_mconsin_lay, x=x, y=y)
mconsin_m1bb = mconsin_lay.bounds(mask=m1.mask)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=mconsin_m1bb)
net_s_prev = net_s
mconnets_m1bb1_prev = mconnets_m1bb1
assert x_s is not None
assert y_s0 is not None
assert x_c is not None
assert y_c0 is not None
assert dy is not None
assert mconacclk_m1bb_last is not None
assert mconasclk_m1bb_last is not None
assert affcvss_lipinbb_last is not None
assert affcvdd_lipinbb_last is not None
assert affsvss_lipinbb_last is not None
assert affsvdd_lipinbb_last is not None
# Place in_captureclk capture flop + connect
x = x_c
y = y_c0 - dy
cclkff_lay = layouter.place(
insts.incaptclkff, x=x, y=y, rotation=_geo.Rotation.R90,
)
cclkffclk_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.sramclk)
cclkffcclk_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
cclkffcclkl_lipinbb = cclkff_lay.bounds(mask=lipin.mask, net=nets.in_captureclk_l)
x = mconacclk_m1bb_last.center.x
y = cclkffcclk_lipinbb.center.y
layouter.add_wire(
net=nets.in_captureclk, wire=mcon, x=x, y=y, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
# Connect sramclk
net = nets.sramclk
_mconclkcclkff_lay = layouter.wire_layout(
net=net, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconclkcclkff_libb = _mconclkcclkff_lay.bounds(mask=li.mask)
x = cclkffclk_lipinbb.right - _mconclkcclkff_libb.right
y = cclkffclk_lipinbb.center.y
layouter.place(_mconclkcclkff_lay, x=x, y=y)
viaclkcclkff_lay = layouter.add_wire(
net=net, wire=via, columns=2, x=x, y=y,
bottom_enclosure="tall", top_enclosure="tall",
)
viaclkcclkff_m2bb = viaclkcclkff_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=viaclkcclkff_m2bb, right=sramclk_m2pinbb.right,
)
layouter.add_wire(net=net, wire=m2, shape=shape)
shape = _geo.Rect.from_rect(
rect=sramclk_m2pinbb, top=viaclkcclkff_m2bb.top,
)
layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
# Connect in_captureclk_l
_mconcclklcclkff_lay = layouter.wire_layout(
net=nets.in_captureclk_l, wire=mcon, columns=2,
bottom_enclosure="wide", top_enclosure="tall",
)
_mconcclklcclkff_libb = _mconcclklcclkff_lay.bounds(mask=li.mask)
x = cclkffcclkl_lipinbb.left - _mconcclklcclkff_libb.left
y = cclkffcclkl_lipinbb.center.y
mconcclklcclkff_lay = layouter.place(_mconcclklcclkff_lay, x=x, y=y)
mconcclklcclkff_m1bb = mconcclklcclkff_lay.bounds(mask=m1.mask)
layouter.add_wire(
net=nets.in_captureclk_l, wire=m1, pin=m1pin, shape=mconcclklcclkff_m1bb,
)
# Compute horizontal displacement of the standard cells
assert self.byte_size > 2
m2pinbb1 = sram_lay.bounds(mask=m2pin.mask, net=nets["d_c[0]"], depth=1)
m2pinbb2 = sram_lay.bounds(mask=m2pin.mask, net=nets["d_c[2]"], depth=1)
dx_stdcell = m2pinbb2.center.x - m2pinbb1.center.x
# Place and connect we, d, q flipflops
insreg_m1bb_prev = None
insreg_net_prev = None
outsreg_m1bb_prev = None
outsreg_net_prev = None
outsreg_m1bb_first = None
outsreg_net_first = None
mconwecclk_m1bb_last = None
mconwesclk_m1bb_last = None
mcondcclk_m1bb1_last = None
mcondcclk_m1bb2_last = None
mcondsclk_m1bb1_last = None
mcondsclk_m1bb2_last = None
mconqcclk_m1bb1_last = None
mconqcclk_m1bb2_last = None
mconqsclk_m1bb1_last = None
mconqsclk_m1bb2_last = None
mcondoc_m1bb_first = None
mcondoc_m1bb_last = None
for n_we in range(self.we_size):
# Place we flops
we_s = nets[f"we_s[{n_we}]"]
we_c = nets[f"we_c[{n_we}]"]
sramwe_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=we_c, depth=1)
_weffc_lay = layouter.inst_layout(inst=insts[f"weff_c[{n_we}]"])
_weffc_bb = _weffc_lay.boundary
assert _weffc_bb is not None
_weffs_lay = layouter.inst_layout(
inst=insts[f"weff_s[{n_we}]"], rotation=_geo.Rotation.MX,
)
_weffs_bb = _weffs_lay.boundary
assert _weffs_bb is not None
stdcell_left = sramwe_m2pinbb.left - 5.0
x = stdcell_left - _weffc_bb.left
y = -5.0 - _weffc_bb.top
weffc_lay = layouter.place(_weffc_lay, x=x, y=y)
weffc_bb = weffc_lay.boundary
assert weffc_bb is not None
weffcclk_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
weffcwes_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=we_s)
weffcwec_lipinbb = weffc_lay.bounds(mask=lipin.mask, net=we_c)
x = stdcell_left - _weffs_bb.left
y = weffc_bb.bottom - _weffs_bb.top
weffs_lay = layouter.place(_weffs_lay, x=x, y=y)
weffs_bb = weffs_lay.boundary
assert weffs_bb is not None
weffsclk_lipinbb = weffs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
weffswes_lipinbb = weffs_lay.bounds(mask=lipin.mask, net=we_s)
# Connect we captureclk
net = nets.in_captureclk
o = weffcclk_lipinbb.center
mconwecclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mconwecclk_m1bb = mconwecclk_lay.bounds(mask=m1.mask)
mconwecclk_m1bb_last = mconwecclk_m1bb
# Connect we shifteclk
net = nets.in_shiftclk
o = weffsclk_lipinbb.center
mconwesclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mconwesclk_m1bb = mconwesclk_lay.bounds(mask=m1.mask)
mconwesclk_m1bb_last = mconwesclk_m1bb
# Connect we_c
_mconwec_lay = layouter.wire_layout(
net=we_c, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconwec_libb = _mconwec_lay.bounds(mask=li.mask)
x = weffcwec_lipinbb.center.x
y = weffcwec_lipinbb.top - _mconwec_libb.top
mconwec_lay = layouter.place(_mconwec_lay, x=x, y=y)
mconwec_m1bb = mconwec_lay.bounds(mask=m1.mask)
x = sramwe_m2pinbb.center.x
viawec_lay = layouter.add_wire(net=we_c, wire=via, rows=2, x=x, y=y)
viawec_m1bb = viawec_lay.bounds(mask=m1.mask)
viawec_m2bb = viawec_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sramwe_m2pinbb, bottom=viawec_m2bb.bottom)
layouter.add_wire(net=we_c, wire=m2, shape=shape)
shape = _geo.Rect.from_rect(rect=mconwec_m1bb, left=viawec_m1bb.left)
layouter.add_wire(net=we_c, wire=m1, shape=shape)
# Connect we_s
_mconwes_lay = layouter.wire_layout(
net=we_s, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconwes_libb = _mconwes_lay.bounds(mask=li.mask)
x = weffswes_lipinbb.center.x
y = weffswes_lipinbb.top - _mconwes_libb.top
mconwes_lay1 = layouter.place(_mconwes_lay, x=x, y=y)
mconwes_m1bb1 = mconwes_lay1.bounds(mask=m1.mask)
x = weffcwes_lipinbb.center.x
y = weffcwes_lipinbb.bottom - _mconwes_libb.bottom
mconwes_lay2 = layouter.place(_mconwes_lay, x=x, y=y)
mconwes_m1bb2 = mconwes_lay2.bounds(mask=m1.mask)
insreg_m1bb_prev = mconwes_m1bb2
insreg_net_prev = we_s
shape = _geo.Rect.from_rect(rect=mconwes_m1bb2, bottom=mconwes_m1bb1.bottom)
layouter.add_wire(net=we_s, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconwes_m1bb1, left=mconwes_m1bb2.left)
layouter.add_wire(net=we_s, wire=m1, shape=shape)
# Place d flops, q flops and muxes
y_d = None
for n_byte in range(self.byte_size):
n_bit = n_we*self.byte_size + n_byte
d_s = nets[f"d_s[{n_bit}]"]
d_c = nets[f"d_c[{n_bit}]"]
sramd_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=d_c, depth=1)
q_c = nets[f"q_c[{n_bit}]"]
q_cl = nets[f"q_cl[{n_bit}]"]
q_mux = nets[f"q_mux[{n_bit}]"]
if n_bit < (self.word_size - 1):
q_s = nets[f"q_s[{n_bit}]"]
else:
q_s = nets.shift_out
sramq_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=q_c, depth=1)
mux_below = (n_byte%2) == 0
# Place the d flops, put two underneath each other
_dffc_lay = layouter.inst_layout(inst=insts[f"dff_c[{n_bit}]"])
_dffc_bb = _dffc_lay.boundary
assert _dffc_bb is not None
_dffs_lay = layouter.inst_layout(
inst=insts[f"dff_s[{n_bit}]"], rotation=_geo.Rotation.MX,
)
_dffs_bb = _dffs_lay.boundary
assert _dffs_bb is not None
# Increase stdcell_left if n_byte is even but not 0;
# for zero we use the stdcell_left determined by the we flops
if mux_below and (n_byte != 0):
stdcell_left += dx_stdcell
if mux_below:
y_d = weffs_bb.bottom - _dffc_bb.top
else:
assert y_d is not None
y_d -= _dffc_bb.height + _dffs_bb.height
x = stdcell_left - _dffc_bb.left
y = y_d
dffc_lay = layouter.place(_dffc_lay, x=x, y=y)
dffc_bb = dffc_lay.boundary
assert dffc_bb is not None
dffcclk_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=nets.in_captureclk)
dffcdc_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=d_c)
dffcds_lipinbb = dffc_lay.bounds(mask=lipin.mask, net=d_s)
y = dffc_bb.bottom - _dffs_bb.top
dffs_lay = layouter.place(_dffs_lay, x=x, y=y)
dffsclk_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=nets.in_shiftclk)
dffsds_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=d_s)
# Connect d captureclk
net = nets.in_captureclk
o = dffcclk_lipinbb.center
mcondcclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mcondcclk_m1bb = mcondcclk_lay.bounds(mask=m1.mask)
if mux_below:
mcondcclk_m1bb1_last = mcondcclk_m1bb
else:
mcondcclk_m1bb2_last = mcondcclk_m1bb
# Connect d shifteclk
net = nets.in_shiftclk
o = dffsclk_lipinbb.center
mcondsclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mcondsclk_m1bb = mcondsclk_lay.bounds(mask=m1.mask)
if mux_below:
mcondsclk_m1bb1_last = mcondsclk_m1bb
else:
mcondsclk_m1bb2_last = mcondsclk_m1bb
# Connect d_c
_mcondc_lay = layouter.wire_layout(
net=d_c, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mcondc_libb = _mcondc_lay.bounds(mask=li.mask)
x = dffcdc_lipinbb.center.x
y = dffcdc_lipinbb.top - _mcondc_libb.top
mcondc_lay = layouter.place(_mcondc_lay, x=x, y=y)
mcondc_m1bb = mcondc_lay.bounds(mask=m1.mask)
x = sramd_m2pinbb.center.x
viadc_lay = layouter.add_wire(net=d_c, wire=via, rows=2, x=x, y=y)
viadc_m1bb = viadc_lay.bounds(mask=m1.mask)
viadc_m2bb = viadc_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sramd_m2pinbb, bottom=viadc_m2bb.bottom)
layouter.add_wire(net=d_c, wire=m2, shape=shape)
shape = _geo.Rect.from_rect(rect=mcondc_m1bb, left=viadc_m1bb.left)
layouter.add_wire(net=d_c, wire=m1, shape=shape)
# Connect d_s
_mconds_lay = layouter.wire_layout(
net=d_s, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconds_libb = _mconds_lay.bounds(mask=li.mask)
x = dffsds_lipinbb.center.x
y = dffsds_lipinbb.top - _mconds_libb.top
mconds_lay1 = layouter.place(_mconds_lay, x=x, y=y)
mconds_m1bb1 = mconds_lay1.bounds(mask=m1.mask)
x = dffcds_lipinbb.center.x
y = dffcds_lipinbb.bottom - _mconds_libb.bottom
mconds_lay2 = layouter.place(_mconds_lay, x=x, y=y)
mconds_m1bb2 = mconds_lay2.bounds(mask=m1.mask)
if (not mux_below) and (n_bit < (self.word_size - 1)):
y = (
dffcds_lipinbb.top - _mconds_libb.top
- mconds_m1bb2.height - 2*m1.min_space
)
mconds_lay3 = layouter.place(_mconds_lay, x=x, y=y)
mconds_m1bb3 = mconds_lay3.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=mconds_m1bb2, bottom=mconds_m1bb1.bottom)
layouter.add_wire(net=d_s, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconds_m1bb1, left=mconds_m1bb2.left)
layouter.add_wire(net=d_s, wire=m1, shape=shape)
# Connect previous output of input shiftregister
assert insreg_m1bb_prev is not None
assert insreg_net_prev is not None
dffssregp_lipinbb = dffs_lay.bounds(mask=lipin.mask, net=insreg_net_prev)
_mconsregp_lay = layouter.wire_layout(
net=insreg_net_prev, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconsregp_libb = _mconsregp_lay.bounds(mask=li.mask)
x = dffssregp_lipinbb.center.x
y = dffssregp_lipinbb.bottom - _mconsregp_libb.bottom
mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
if n_byte == 0:
# Straight line for we_s
viasregp_lay1 = layouter.add_wire(
net=insreg_net_prev, wire=via, rows=2,
origin=insreg_m1bb_prev.center,
)
viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
viasregp_lay2 = layouter.add_wire(
net=insreg_net_prev, wire=via, rows=2,
origin=mconsregp_m1bb.center,
)
viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
)
layouter.add_wire(net=insreg_net_prev, wire=m2, shape=shape)
elif not mux_below:
# Horizontal m1, vertical m2
viasregp_lay1 = layouter.add_wire(
net=insreg_net_prev, wire=via, rows=2,
origin=insreg_m1bb_prev.center,
)
viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
x = insreg_m1bb_prev.center.x
y = mconsregp_m1bb.center.y
viasregp_lay2 = layouter.add_wire(
net=insreg_net_prev, wire=via, rows=2, x=x, y=y,
)
viasregp_m1bb2 = viasregp_lay2.bounds(mask=m1.mask)
viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=mconsregp_m1bb, right=viasregp_m1bb2.right,
)
layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(
rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
)
layouter.add_wire(net=insreg_net_prev, wire=m2, shape=shape)
else: # mux_below and n_byte > 0
# Connect from previous column with a polygon on m1
left = stdcell_left
right = left + insreg_m1bb_prev.height
shape = _geo.Polygon.from_floats(points=(
(insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
(insreg_m1bb_prev.left, insreg_m1bb_prev.top),
(left, insreg_m1bb_prev.top),
(left, mconsregp_m1bb.top),
(mconsregp_m1bb.right, mconsregp_m1bb.top),
(mconsregp_m1bb.right, mconsregp_m1bb.bottom),
(right, mconsregp_m1bb.bottom),
(right, insreg_m1bb_prev.bottom),
(insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
))
layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
if mux_below or (n_bit == (self.word_size - 1)):
insreg_m1bb_prev = mconds_m1bb1
else:
insreg_m1bb_prev = mconds_m1bb3
insreg_net_prev = d_s
# Place the q flops, put two underneath each other
if mux_below:
_qffc_lay = layouter.inst_layout(
inst=insts[f"qff_c[{n_bit}]"],
)
_qffc_bb = _qffc_lay.boundary
assert _qffc_bb is not None
_qffs_lay = layouter.inst_layout(
inst=insts[f"qff_s[{n_bit}]"], rotation=_geo.Rotation.MX,
)
_qffs_bb = _qffs_lay.boundary
assert _qffs_bb is not None
_qmux_lay = layouter.inst_layout(
inst=insts[f"qmux[{n_bit}]"],
)
_qmux_bb = _qmux_lay.boundary
assert _qmux_bb is not None
y_q = y_d - 4*_qffc_bb.height
x_mux = stdcell_left - _qmux_bb.left
y_mux = y_q - 2*_qffc_bb.height
else:
_qffc_lay = layouter.inst_layout(
inst=insts[f"qff_c[{n_bit}]"], rotation=_geo.Rotation.MX,
)
_qffc_bb = _qffc_lay.boundary
assert _qffc_bb is not None
_qffs_lay = layouter.inst_layout(
inst=insts[f"qff_s[{n_bit}]"],
)
_qffs_bb = _qffs_lay.boundary
assert _qffs_bb is not None
_qmux_lay = layouter.inst_layout(
inst=insts[f"qmux[{n_bit}]"],
)
_qmux_bb = _qmux_lay.boundary
assert _qmux_bb is not None
x_mux = stdcell_left - _qmux_bb.left + _qmux_bb.width
y_mux = y_d - 4*_qffc_bb.height
y_q = y_mux
x = stdcell_left - _qffc_bb.left
y = y_q
qffc_lay = layouter.place(_qffc_lay, x=x, y=y)
qffc_bb = qffc_lay.boundary
assert qffc_bb is not None
qffcclk_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=nets.out_captureclk)
qffcqc_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=q_c)
qffcqcl_lipinbb = qffc_lay.bounds(mask=lipin.mask, net=q_cl)
y = qffc_bb.bottom - _qffs_bb.top
qffs_lay = layouter.place(_qffs_lay, x=x, y=y)
qffsclk_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=nets.out_shiftclk)
qffsqs_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=q_s)
qffsqmux_lipinbb = qffs_lay.bounds(mask=lipin.mask, net=q_mux)
qmux_lay = layouter.place(_qmux_lay, x=x_mux, y=y_mux)
qmuxqmux_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=q_mux)
qmuxqcl_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=q_cl)
qmuxdoc_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=nets.out_docapture)
# Connect q captureclk
net = nets.out_captureclk
o = qffcclk_lipinbb.center
mconqcclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mconqcclk_m1bb = mconqcclk_lay.bounds(mask=m1.mask)
if mux_below:
mconqcclk_m1bb1_last = mconqcclk_m1bb
else:
mconqcclk_m1bb2_last = mconqcclk_m1bb
# Connect q shifteclk
net = nets.out_shiftclk
o = qffsclk_lipinbb.center
mconqsclk_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mconqsclk_m1bb = mconqsclk_lay.bounds(mask=m1.mask)
if mux_below:
mconqsclk_m1bb1_last = mconqsclk_m1bb
else:
mconqsclk_m1bb2_last = mconqsclk_m1bb
# Connect q_c
net = q_c
_mconqc_lay = layouter.wire_layout(
net=net, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconqc_libb = _mconqc_lay.bounds(mask=li.mask)
x = qffcqc_lipinbb.center.x
y = qffcqc_lipinbb.top - _mconqc_libb.top
mconqc_lay = layouter.place(_mconqc_lay, x=x, y=y)
mconqc_m1bb = mconqc_lay.bounds(mask=m1.mask)
x = sramq_m2pinbb.center.x
viaqc_lay = layouter.add_wire(net=net, wire=via, rows=2, x=x, y=y)
viaqc_m1bb = viaqc_lay.bounds(mask=m1.mask)
viaqc_m2bb = viaqc_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sramq_m2pinbb, bottom=viaqc_m2bb.bottom)
layouter.add_wire(net=net, wire=m2, shape=shape)
if (mconqc_m1bb.left > viaqc_m1bb.left):
shape = _geo.Rect.from_rect(rect=mconqc_m1bb, left=viaqc_m1bb.left)
else:
shape = _geo.Rect.from_rect(rect=mconqc_m1bb, right=viaqc_m1bb.right)
layouter.add_wire(net=net, wire=m1, shape=shape)
# Connect q_cl
net = q_cl
_mconqcl_lay = layouter.wire_layout(
net=net, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconqcl_libb = _mconqcl_lay.bounds(mask=li.mask)
x = qffcqcl_lipinbb.center.x
if mux_below:
y = qffcqcl_lipinbb.bottom - _mconqcl_libb.bottom
else:
y = qffcqcl_lipinbb.top - _mconqcl_libb.top
mconqcl_lay = layouter.place(_mconqcl_lay, x=x, y=y)
mconqcl_m1bb = mconqcl_lay.bounds(mask=m1.mask)
x = qmuxqcl_lipinbb.center.x
viaqcl_lay1 = layouter.add_wire(
net=net, wire=via, rows=2, x=x, y=y,
bottom_enclosure="wide", top_enclosure="tall",
)
viaqcl_m1bb1 = viaqcl_lay1.bounds(mask=m1.mask)
viaqcl_m2bb1 = viaqcl_lay1.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=mconqcl_m1bb, left = viaqcl_m1bb1.left,
)
layouter.add_wire(net=net, wire=m1, shape=shape)
x = qmuxqcl_lipinbb.center.x
if mux_below:
y = qmuxqcl_lipinbb.top - _mconqcl_libb.top
else:
y = qmuxqcl_lipinbb.bottom - _mconqcl_libb.bottom
# Use second row, i0 has already higher bottom
y += 2*m1.min_space
layouter.place(_mconqcl_lay, x=x, y=y)
viaqcl_lay2 = layouter.add_wire(
net=net, wire=via, rows=2, x=x, y=y,
bottom_enclosure="wide", top_enclosure="tall",
)
viaqcl_m2bb2 = viaqcl_lay2.bounds(mask=m2.mask)
if mux_below:
shape = _geo.Rect.from_rect(
rect=viaqcl_m2bb1, bottom=viaqcl_m2bb2.bottom,
)
else:
shape = _geo.Rect.from_rect(
rect=viaqcl_m2bb1, top=viaqcl_m2bb2.top,
)
layouter.add_wire(net=net, wire=m2, shape=shape)
# Connect q_s
_mconqs_lay = layouter.wire_layout(
net=q_s, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconqs_libb = _mconqs_lay.bounds(mask=li.mask)
x = qffsqs_lipinbb.center.x
if mux_below:
y = qffsqs_lipinbb.bottom - _mconqs_libb.bottom
else:
y = qffsqs_lipinbb.top - _mconqs_libb.top
mconqs_lay1 = layouter.place(_mconqs_lay, x=x, y=y)
mconqs_m1bb1 = mconqs_lay1.bounds(mask=m1.mask)
# Connect out_docapture
net = nets.out_docapture
o = self.tech.on_grid(qmuxdoc_lipinbb.center, mult=2)
mcondoc_lay = layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
mcondoc_m1bb = mcondoc_lay.bounds(mask=m1.mask)
if n_bit == 0:
mcondoc_m1bb_first = mcondoc_m1bb
mcondoc_m1bb_last = mcondc_m1bb
# Connect q_mux
_mconqmux_lay = layouter.wire_layout(
net=q_mux, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconqmux_libb = _mconqmux_lay.bounds(mask=li.mask)
x = qffsqmux_lipinbb.center.x
if mux_below:
y = qffsqmux_lipinbb.bottom - _mconqmux_libb.bottom
else:
y = qffsqmux_lipinbb.top - _mconqmux_libb.top
mconqmux_lay1 = layouter.place(_mconqmux_lay, x=x, y=y)
mconqmux_m1bb1 = mconqmux_lay1.bounds(mask=m1.mask)
x = qmuxqmux_lipinbb.center.x
if mux_below:
y = qmuxqmux_lipinbb.top - _mconqmux_libb.top
else:
y = qmuxqmux_lipinbb.bottom - _mconqmux_libb.bottom
mconqmux_lay2 = layouter.place(_mconqmux_lay, x=x, y=y)
mconqmux_m1bb2 = mconqmux_lay2.bounds(mask=m1.mask)
assert mconqmux_m1bb1.left < mconqmux_m1bb2.left
if mux_below:
# Connect on m1 with polygon
shape = _geo.Polygon.from_floats(points=(
(mconqmux_m1bb1.left, mconqmux_m1bb2.bottom),
(mconqmux_m1bb1.left, mconqmux_m1bb1.top),
(mconqmux_m1bb1.right, mconqmux_m1bb1.top),
(mconqmux_m1bb1.right, mconqmux_m1bb2.top),
(mconqmux_m1bb2.right, mconqmux_m1bb2.top),
(mconqmux_m1bb2.right, mconqmux_m1bb2.bottom),
(mconqmux_m1bb1.left, mconqmux_m1bb2.bottom),
))
layouter.add_wire(net=q_mux, wire=m1, shape=shape)
else:
# Horizontal m1, vertical m2
shape = _geo.Rect.from_rect(
rect=mconqmux_m1bb2, left=mconqmux_m1bb1.left,
)
layouter.add_wire(net=q_mux, wire=m1, shape=shape)
x = mconqmux_m1bb1.center.x
y = mconqmux_m1bb2.center.y
viaqmux_lay1 = layouter.add_wire(
net=q_mux, wire=via, rows=2, x=x, y=y,
bottom_enclosure="tall", top_enclosure="tall",
)
viaqmux_m2bb1 = viaqmux_lay1.bounds(mask=m2.mask)
o = mconqmux_m1bb1.center
viaqmux_lay2 = layouter.add_wire(
net=q_mux, wire=via, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="tall",
)
viaqmux_m2bb2 = viaqmux_lay2.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=viaqmux_m2bb1, bottom=viaqmux_m2bb2.bottom,
)
layouter.add_wire(net=q_mux, wire=m2, shape=shape)
# Connect previous output of out shiftregister
if outsreg_m1bb_prev is None:
assert outsreg_net_prev is None
assert n_byte == 0 # Can only happen for first bit in byte
outsreg_net_first = net = nets[f"d_s[{self.word_size - 1}]"]
qmuxds_lipinbb = qmux_lay.bounds(mask=lipin.mask, net=net)
_mconds_lay = layouter.wire_layout(
net=net, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconds_libb = _mconds_lay.bounds(mask=li.mask)
x = qmuxds_lipinbb.center.x
y = (
qmuxds_lipinbb.top - _mconds_libb.top
- _mconds_libb.height - 2*m1.min_space
)
mconds_lay = layouter.place(_mconds_lay, x=x, y=y)
outsreg_m1bb_first = mconds_lay.bounds(mask=m1.mask)
else:
assert outsreg_net_prev is not None
qmuxsregp_lipinbb = qmux_lay.bounds(
mask=lipin.mask, net=outsreg_net_prev,
)
_mconsregp_lay = layouter.wire_layout(
net=insreg_net_prev, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconsregp_libb = _mconsregp_lay.bounds(mask=li.mask)
if mux_below:
x = qmuxsregp_lipinbb.center.x
y = (
qmuxsregp_lipinbb.bottom - _mconsregp_libb.bottom
+ _mconsregp_libb.height + 2*m1.min_space
)
mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
x = outsreg_m1bb_prev.center.x
viasregp_lay1 = layouter.add_wire(
net=outsreg_net_prev, wire=via, rows=2, x=x, y=y,
bottom_enclosure="wide", top_enclosure="tall",
)
viasregp_m1bb1 = viasregp_lay1.bounds(mask=m1.mask)
viasregp_m2bb1 = viasregp_lay1.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=mconsregp_m1bb, left=viasregp_m1bb1.left,
)
layouter.add_wire(
net=outsreg_net_prev, wire=m1, shape=shape,
)
y = outsreg_m1bb_prev.center.y
viasregp_lay2 = layouter.add_wire(
net=outsreg_net_prev, wire=via, rows=2, x=x, y=y,
bottom_enclosure="wide", top_enclosure="tall",
)
viasregp_m2bb2 = viasregp_lay2.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=viasregp_m2bb1, bottom=viasregp_m2bb2.bottom,
)
layouter.add_wire(net=outsreg_net_prev, wire=m2, shape=shape)
else:
x = qmuxsregp_lipinbb.center.x
y = qmuxsregp_lipinbb.top - _mconsregp_libb.top
mconsregp_lay = layouter.place(_mconsregp_lay, x=x, y=y)
mconsregp_m1bb = mconsregp_lay.bounds(mask=m1.mask)
shape = _geo.Polygon.from_floats(points=(
(mconsregp_m1bb.left, mconsregp_m1bb.bottom),
(mconsregp_m1bb.left, outsreg_m1bb_prev.top),
(outsreg_m1bb_prev.right, outsreg_m1bb_prev.top),
(outsreg_m1bb_prev.right, outsreg_m1bb_prev.bottom),
(mconsregp_m1bb.right, outsreg_m1bb_prev.bottom),
(mconsregp_m1bb.right, mconsregp_m1bb.bottom),
(mconsregp_m1bb.left, mconsregp_m1bb.bottom),
))
layouter.add_wire(net=outsreg_net_prev, wire=m1, shape=shape)
outsreg_m1bb_prev = mconqs_m1bb1
outsreg_net_prev = q_s
# Place the out captureclk flop if the first bit
if n_bit == 0:
_cclkff_lay = layouter.inst_layout(inst=insts.outcaptclkff)
_cclkff_bb = _cclkff_lay.boundary
assert _cclkff_bb is not None
x = qffc_bb.left - _cclkff_bb.right
y = qffc_bb.bottom - _cclkff_bb.bottom
cclkff_lay = layouter.place(_cclkff_lay, x=x, y=y)
cclkffclk_lipinbb = cclkff_lay.bounds(
mask=lipin.mask, net=nets.sramclk,
)
cclkffcclk_lipinbb = cclkff_lay.bounds(
mask=lipin.mask, net=nets.out_captureclk,
)
cclkffcclkl_lipinbb = cclkff_lay.bounds(
mask=lipin.mask, net=nets.out_captureclk_l,
)
# Connedt sramclk
net = nets.sramclk
_mconclk_lay = layouter.wire_layout(
net=net, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconclk_libb = _mconclk_lay.bounds(mask=li.mask)
x = cclkffclk_lipinbb.center.x
y = cclkffclk_lipinbb.bottom - _mconclk_libb.bottom
mconclk_lay = layouter.place(_mconclk_lay, x=x, y=y)
mconclk_m1bb = mconclk_lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(
rect=mconclk_m1bb, left=sramclk_m2pinbb.left,
)
layouter.add_wire(net=net, wire=m1, shape=shape)
x = sramclk_m2pinbb.center.x
y = mconclk_m1bb.center.y
viaclk_lay = layouter.add_wire(
net=net, wire=via, x=x, y=y,
top_width=sramclk_m2pinbb.width,
)
viaclk_m2bb = viaclk_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(
rect=viaclk_m2bb, top=sramclk_m2pinbb.top
)
layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
# Connect out_captureclk
net = nets.out_captureclk
o = cclkffcclk_lipinbb.center
layouter.add_wire(
net=net, wire=mcon, rows=2, origin=o,
bottom_enclosure="tall", top_enclosure="wide",
)
# Connect out_captureclk_l
net = nets.out_captureclk_l
_mconcclkl_lay = layouter.wire_layout(
net=net, wire=mcon, rows=2,
bottom_enclosure="tall", top_enclosure="wide",
)
_mconcclkl_libb = _mconcclkl_lay.bounds(mask=li.mask)
x = cclkffcclkl_lipinbb.center.x
y = cclkffcclkl_lipinbb.bottom - _mconcclkl_libb.bottom
mconcclkl_lay = layouter.place(_mconcclkl_lay, x=x, y=y)
mconcclkl_m1bb = mconcclkl_lay.bounds(mask=m1.mask)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=mconcclkl_m1bb)
# Connect output of in shift register to first of output shift register
assert insreg_m1bb_prev is not None
assert insreg_net_prev is not None
assert outsreg_m1bb_first is not None
assert outsreg_net_first is not None
assert insreg_net_prev == outsreg_net_first
left = insreg_m1bb_prev.right + 2*m1.min_space
right = left + insreg_m1bb_prev.height
shape = _geo.Polygon.from_floats(points=(
(outsreg_m1bb_first.left, outsreg_m1bb_first.bottom),
(outsreg_m1bb_first.left, outsreg_m1bb_first.top),
(left, outsreg_m1bb_first.top),
(left, insreg_m1bb_prev.bottom),
(insreg_m1bb_prev.left, insreg_m1bb_prev.bottom),
(insreg_m1bb_prev.left, insreg_m1bb_prev.top),
(right, insreg_m1bb_prev.top),
(right, outsreg_m1bb_first.bottom),
(outsreg_m1bb_first.left, outsreg_m1bb_first.bottom),
))
layouter.add_wire(net=insreg_net_prev, wire=m1, shape=shape)
assert mconwecclk_m1bb_last is not None
assert mconwesclk_m1bb_last is not None
assert mcondcclk_m1bb1_last is not None
assert mcondcclk_m1bb2_last is not None
assert mcondsclk_m1bb1_last is not None
assert mcondsclk_m1bb2_last is not None
assert mconqcclk_m1bb1_last is not None
assert mconqcclk_m1bb2_last is not None
assert mconqsclk_m1bb1_last is not None
assert mconqsclk_m1bb2_last is not None
# Connect shift_out
assert outsreg_m1bb_prev is not None
assert outsreg_net_prev is not None
assert outsreg_net_prev == nets.shift_out
layouter.add_wire(
net=outsreg_net_prev, wire=m1, pin=m1pin, shape=outsreg_m1bb_prev,
)
# Conect in_captureclk
net = nets.in_captureclk
bottom = mconwecclk_m1bb_last.bottom
left = mconacclk_m1bb_last.left
shape = _geo.Rect.from_rect(rect=mconacclk_m1bb_last, bottom=bottom)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconwecclk_m1bb_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mcondcclk_m1bb1_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mcondcclk_m1bb2_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
x = mconacclk_m1bb_last.center.x
y = mconwecclk_m1bb_last.center.y
via_lay = layouter.add_wire(
net=net, wire=via, rows=2, columns=2, x=x, y=y,
)
via_m2bb1 = via_lay.bounds(mask=m2.mask)
y = mcondcclk_m1bb1_last.center.y
layouter.add_wire(
net=net, wire=via, rows=2, columns=2, x=x, y=y,
)
y = mcondcclk_m1bb2_last.center.y
via_lay = layouter.add_wire(
net=net, wire=via, rows=2, columns=2, x=x, y=y,
)
via_m2bb2 = via_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=via_m2bb1, bottom=via_m2bb2.bottom)
layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
# Conect in_shiftclk
net = nets.in_shiftclk
bottom = mcondsclk_m1bb2_last.bottom
left = mconasclk_m1bb_last.left
shape = _geo.Rect.from_rect(rect=mconasclk_m1bb_last, bottom=bottom)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
shape = _geo.Rect.from_rect(rect=mconwesclk_m1bb_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mcondsclk_m1bb1_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mcondsclk_m1bb2_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
# Conect out_captureclk
net = nets.out_captureclk
left = 5.0
shape = _geo.Rect.from_rect(rect=mconqcclk_m1bb1_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconqcclk_m1bb2_last, left=left)
layouter.add_wire(net=net, wire=m1, shape=shape)
right = 6.0
shape = _geo.Rect(
left=left, bottom=mconqcclk_m1bb2_last.bottom,
right=right, top=mconqcclk_m1bb1_last.top,
)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
# Conect out_shiftclk
net = nets.out_shiftclk
x = 27.0
shape = _geo.Rect.from_rect(rect=mconqsclk_m1bb1_last, left=x)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=mconqsclk_m1bb2_last, left=x)
layouter.add_wire(net=net, wire=m1, shape=shape)
y = mconqsclk_m1bb1_last.center.y
via_lay = layouter.add_wire(
net=net, wire=via, rows=2, columns=2, x=x, y=y,
)
via_m2bb1 = via_lay.bounds(mask=m2.mask)
y = mconqsclk_m1bb2_last.center.y
via_lay = layouter.add_wire(
net=net, wire=via, rows=2, columns=2, x=x, y=y,
)
via_m2bb2 = via_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=via_m2bb1, bottom=via_m2bb2.bottom)
layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
# Connect out_docapture
net = nets.out_docapture
assert mcondoc_m1bb_first is not None
assert mcondoc_m1bb_last is not None
shape = _geo.Rect.from_rect(
rect=mcondoc_m1bb_first, right=mcondoc_m1bb_last.right,
)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
# TODO: remove hard coded values
stdcell_height = 10.0
stdcellvss_height = 1.2
stdcellvdd_height = 1.2
stdcellnwelledge_height = 4.8
bottomblock_top = -5
# Connect dvss
net = nets.dvss
bottom = -115
shape = _geo.Rect.from_rect(rect=affsvss_lipinbb_last, bottom=bottom)
layouter.add_wire(net=net, wire=li, shape=shape)
shape = _geo.Rect.from_rect(rect=affcvss_lipinbb_last, bottom=bottom)
layouter.add_wire(net=net, wire=li, shape=shape)
left = affcvss_lipinbb_last.left
right = sram_bb.right
for i in range(6):
top = bottomblock_top - (2*i + 1)*stdcell_height + stdcellvss_height
if i < 5:
bottom = top - 2*stdcellvss_height
else:
bottom = top - stdcellvss_height
shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
layouter.add_wire(net=net, wire=li, shape=shape)
# Connect with first column
if i == 4:
x = affcvss_lipinbb_last.left
y = 0.5*(bottom + top)
mcon_lay = layouter.add_wire(
net=net, wire=mcon, rows=7, columns=7, x=x, y=y
)
mcon_m1bb1 = mcon_lay.bounds(mask=m1.mask)
x = affsvss_lipinbb_last.center.x
mcon_lay = layouter.add_wire(
net=net, wire=mcon, rows=7, columns=7, x=x, y=y
)
mcon_m1bb2 = mcon_lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=mcon_m1bb1, left=mcon_m1bb2.left)
layouter.add_wire(net=net, wire=m1, pin=m1pin, shape=shape)
# Connect dvdd
net = nets.dvdd
bottom = -115
shape = _geo.Rect.from_rect(rect=affsvdd_lipinbb_last, bottom=bottom)
layouter.add_wire(net=net, wire=li, shape=shape)
shape = _geo.Rect.from_rect(rect=affcvdd_lipinbb_last, bottom=bottom)
layouter.add_wire(net=net, wire=li, shape=shape)
left = sram_bb.left
right = sram_bb.right
for i in range(6):
# li
bottom = bottomblock_top - 2*i*stdcell_height - stdcellvdd_height
if i == 0:
top = bottom + stdcellvdd_height
else:
top = bottom + 2*stdcellvdd_height
shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
layouter.add_wire(net=net, wire=li, shape=shape)
# nwell
bottom = bottomblock_top - (2*i + 1)*stdcell_height + stdcellnwelledge_height
if i == 0:
top = bottomblock_top + 1.0
else:
top = bottomblock_top - (2*i - 1)*stdcell_height - stdcellnwelledge_height
shape = _geo.Rect(left=left, bottom=bottom, right=right, top=top)
layouter.add_wire(net=net, wire=nwm, shape=shape)
x = left + 1.0
y = 0.5*(bottom + top)
mcon_lay = layouter.add_wire(
net=net, wire=mcon, rows=7, columns=7, x=x, y=y
)
mcon_m1bb1 = mcon_lay.bounds(mask=m1.mask)
via_lay = layouter.add_wire(
net=net, wire=via, rows=4, columns=4, x=x, y=y,
)
via_m2bb = via_lay.bounds(mask=m2.mask)
if i == 5:
# Connect with column
x = affsvdd_lipinbb_last.right
mcon_lay = layouter.add_wire(
net=net, wire=mcon, rows=7, columns=7, x=x, y=y
)
mcon_m1bb2 = mcon_lay.bounds(mask=m1.mask)
shape = _geo.Rect.from_rect(rect=mcon_m1bb1, left=mcon_m1bb2.left)
layouter.add_wire(net=net, wire=m1, shape=shape)
shape = _geo.Rect.from_rect(rect=via_m2bb, top=bottomblock_top)
layouter.add_wire(net=net, wire=m2, pin=m2pin, shape=shape)
# Boundary
layouter.layout.boundary = _geo.Rect(
left=affsvss_lipinbb_last.left,
bottom=(bottomblock_top - 11*stdcell_height),
right = sram_bb.right,
top = sram_bb.top,
)
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
spmem_fab = sky130.Sky130SP6TFactory(lib=self.lib)
dpmem_fab = sky130.Sky130DP8TFactory(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
word_size = 8
we_size = 1
address_groups = (3, 4, 2)
a_bits = sum(address_groups)
spsram_cell = spmem_fab.block(
address_groups=(3, 4, 2), word_size=word_size, we_size=we_size,
cell_name="512x8",
)
dpsram_cell = dpmem_fab.block(
address_groups=(3, 4, 2), word_size=word_size, we_size=we_size,
cell_name="512x8",
)
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
spsram = ckt.instantiate(spsram_cell, name="sram")
dpsram = ckt.instantiate(dpsram_cell, name="sram")
# place
_spsram_lay = layouter.inst_layout(inst=spsram)
_spsram_bb = _spsram_lay.boundary
assert _spsram_bb is not None
_dpsram_lay = layouter.inst_layout(inst=dpsram)
_dpsram_bb = _dpsram_lay.boundary
assert _dpsram_bb is not None
x = sky130.tech.on_grid(_frm.boundary.center.x - _spsram_bb.center.x)
y = _frm.boundary.top - 100.0 - _spsram_bb.top
spsram_lay = layouter.place(_spsram_lay, x=x, y=y)
spsram_bb = spsram_lay.boundary
assert spsram_bb is not None
x = spsram_bb.right + 100.0
dpsram_lay = layouter.place(_dpsram_lay, x=x, y=y)
dpsram_bb = dpsram_lay.boundary
assert dpsram_bb is not None
# Temporary add
spchar_cell = SPCharacterizationWrapper(lib=lib, spcell=spsram_cell)
spchar = ckt.instantiate(spchar_cell, name="sramchar")
x = spsram_bb.right + 700
spchar_lay = layouter.place(
spchar, x=x, y=(y + 50), rotation=_geo.Rotation.R90,
)
# 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 = spsram_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 = spsram_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 = spsram_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 = spsram.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 spsram_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 = spsram.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 spsram_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(a_bits))), # Reversed for a_col
"clk", "we[0]",
*(f"d[{bit}]" for bit in range(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 = spsram.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 = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1)
w = 10.0
right = spsram_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 = spsram_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(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 = spsram.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 = spsram_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