blob: e3a590de5051a611034fbd5188bc606dd47f58fc [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_in", io_number=13),
_io_spec(sram_signal="d[7]", io_type="io_out", 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_in", io_number=9),
_io_spec(sram_signal="d[5]", io_type="io_out", 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_in", io_number=5),
_io_spec(sram_signal="d[3]", io_type="io_out", 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_in", 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_out", 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="vss", io_type="io_analog", io_number=4),
_io_spec(sram_signal="vdd", 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._OnDemandCell[_lbry.Library]):
def __init__(self, *, lib: _lbry.Library):
super().__init__(lib=lib, name="ConnectedSRAM")
def _create_circuit(self):
stdcells = sky130.stdcelllib.cells
mem_fab = sky130.Sky130SP6TFactory(lib=self.lib)
zero_cell = stdcells.zero_x1
one_cell = stdcells.one_x1
tie_cell = stdcells.tie_diff_w4
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()
sram = ckt.instantiate(sram_cell, name="sram")
for spec in io_specs:
prefix = spec.prefix
sram_port = sram.ports[spec.sram_signal]
ckt.new_net(name=spec.toppin_name, external=True, childports=sram_port)
oeb = spec.oeb
if oeb is not None:
num = spec.io_number
if oeb:
one = ckt.instantiate(one_cell, name=f"{prefix}one")
ckt.new_net(
name=f"io_oeb[{num}]", external=True, childports=one.ports.one,
)
else:
zero = ckt.instantiate(zero_cell, name=f"{prefix}zero")
ckt.new_net(
name=f"io_oeb[{num}]", external=True, childports=zero.ports.zero,
)
if spec.io_type != "io_analog":
ckt.instantiate(tie_cell, name=f"{prefix}tie")
def _create_layout(self):
tech = sky130.tech
prims = tech.primitives
ckt = self.circuit
nets = ckt.nets
insts = ckt.instances
li = cast(_prm.MetalWire, prims.li)
lipin = cast(_prm.Marker, prims["li.pin"])
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)
layouter = self.new_circuitlayouter()
layout = layouter.layout
_sram_lay = layouter.inst_layout(inst=insts.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
# vss
spec = io_sig2spec["vss"]
net = nets[spec.toppin_name]
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)
# vdd
spec = io_sig2spec["vdd"]
net = nets[spec.toppin_name]
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)
# a
col = 0
w = 10.0
for a_bit in reversed(range(self.a_bits)):
spec = io_sig2spec[f"a[{a_bit}]"]
assert spec.io_type == "io_in", "Internal error"
net = nets[spec.toppin_name]
sram_m1pinbb = sram_lay.bounds(mask=m1pin.mask, net=net, depth=1)
toppin_bb = _frm.toppins[spec.toppin_name]
right = sram_bb.left - (2*col + 1)*w
left = right - 1
col += 1
x_via = right - w/2.0
y_via = tech.on_grid(sram_m1pinbb.center.y)
via_lay = layouter.add_wire(
net=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)
via_m2bb = via_lay.bounds(mask=m2.mask)
shape = _geo.Rect.from_rect(rect=sram_m1pinbb, left=via_m1bb.left)
layouter.add_wire(net=net, wire=m1, shape=shape)
x_via2 = x_via
y_via2 = toppin_bb.center.y
via2_lay = 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",
)
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=net, wire=m2, shape=shape)
shape = _geo.Rect.from_rect(rect=toppin_bb, right=via2_m3bb.right)
layouter.add_wire(net=net, wire=m3, shape=shape)
# The rest of the pin on m2
for sram_signame in (
"clk", "we[0]",
*(f"d[{bit}]" for bit in range(self.word_size)),
*(f"q[{bit}]" for bit in range(self.word_size)),
):
spec = io_sig2spec[sram_signame]
net = nets[spec.toppin_name]
sram_m2pinbb = sram_lay.bounds(mask=m2pin.mask, net=net, depth=1)
toppin_bb = _frm.toppins[spec.toppin_name]
_via2_lay = layouter.add_wire(
net=net, wire=via2, rows=6, columns=6,
)
_via2_m2bb = _via2_lay.bounds(mask=m2.mask)
if toppin_bb.center.x > sram_m2pinbb.center.x:
x_via2 = sram_m2pinbb.left - _via2_m2bb.left
else:
x_via2 = sram_m2pinbb.right - _via2_m2bb.right
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=sram_m2pinbb, bottom=via2_m2bb.bottom)
layouter.add_wire(net=net, wire=m2, shape=shape)
if toppin_bb.center.x > sram_m2pinbb.center.x:
shape = _geo.Rect.from_rect(rect=toppin_bb, left=via2_m3bb.left)
else:
shape = _geo.Rect.from_rect(rect=toppin_bb, right=via2_m3bb.right)
layouter.add_wire(net=net, wire=m3, shape=shape)
# boundary
layout.boundary = _frm.boundary