| # 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 |