| # 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_spspecs = ( |
| _io_spec(sram_signal="in_shiftclk", io_type="io_in", io_number=14), |
| _io_spec(sram_signal="shift_in", io_type="io_in", io_number=15), |
| _io_spec(sram_signal="in_captureclk_l", io_type="io_out", io_number=16), |
| _io_spec(sram_signal="in_captureclk", io_type="io_in", io_number=17), |
| _io_spec(sram_signal="sramclk", io_type="io_in", io_number=18), |
| _io_spec(sram_signal="out_captureclk_l", io_type="io_out", io_number=13), |
| _io_spec(sram_signal="out_captureclk", io_type="io_in", io_number=12), |
| _io_spec(sram_signal="out_docapture", io_type="io_in", io_number=11), |
| _io_spec(sram_signal="out_shiftclk", io_type="io_in", io_number=10), |
| _io_spec(sram_signal="shift_out", io_type="io_out", io_number=9), |
| ) |
| io_spsig2spec = { |
| spec.sram_signal: spec |
| for spec in io_spspecs |
| } |
| io_sppin2spec = { |
| spec.toppin_name: spec |
| for spec in io_spspecs |
| } |
| |
| |
| io_specs = ( |
| _io_spec(sram_signal="svdd", io_type="io_analog", io_number=4), |
| _io_spec(sram_signal="svss", 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] |
| via2 = cast(_prm.Via, prims.via2) |
| m3 = cast(_prm.MetalWire, prims.m3) |
| assert m3.pin is not None |
| m3pin = m3.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 svss |
| net = nets.svss |
| |
| 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) |
| layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=shape) |
| |
| # Connect svdd |
| net = nets.svdd |
| |
| 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) |
| layouter.add_wire(net=net, wire=m3, pin=m3pin, shape=shape) |
| |
| # 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"]) |
| via3 = cast(_prm.Via, prims.via3) |
| m4 = cast(_prm.MetalWire, prims.m4) |
| |
| 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", |
| ) |
| spchar_cell = SPCharacterizationWrapper(lib=lib, spcell=spsram_cell) |
| 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(spchar_cell, name="sram") |
| dpsram = ckt.instantiate(dpsram_cell, name="sram") |
| |
| dvss.childports += spsram.ports.dvss |
| dvdd.childports += spsram.ports.dvdd |
| |
| # 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 - 60.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 = sky130.tech.on_grid(_frm.boundary.center.x - _dpsram_bb.center.x) |
| y = 1600.0 - _dpsram_bb.bottom |
| dpsram_lay = layouter.place(_dpsram_lay, x=x, y=y) |
| dpsram_bb = dpsram_lay.boundary |
| assert dpsram_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) |
| |
| # Connect the SRAM signals |
| # |
| # svss |
| spec = io_sig2spec["svss"] |
| 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] |
| sram_m3pinbb = spsram_lay.bounds(mask=m3pin.mask, net=net, depth=1) |
| left = sram_m3pinbb.left |
| right = sram_m3pinbb.right |
| top = sram_m3pinbb.top |
| |
| shape = _geo.Rect.from_rect(rect=sram_m3pinbb, top=(sram_m3pinbb.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) |
| |
| # svdd |
| spec = io_sig2spec["svdd"] |
| 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] |
| sram_m3pinbb = spsram_lay.bounds(mask=m3pin.mask, net=net, depth=1) |
| left = sram_m3pinbb.left |
| right = sram_m3pinbb.right |
| top = sram_m3pinbb.top |
| |
| shape = _geo.Rect.from_rect(rect=sram_m3pinbb, top=(sram_m3pinbb.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) |
| |
| # |
| # Support functions |
| # |
| def place_ioperiph(*, spec: _io_spec, sram: _ckt._CellInstance): |
| sig_name = spec.sram_signal |
| prefix = spec.prefix |
| num = spec.io_number |
| |
| pin_name = spec.toppin_name |
| oeb_name = f"io_oeb[{num}]" |
| out_name = f"io_out[{num}]" |
| sram_port = sram.ports[sig_name] |
| |
| if spec.io_type == "io_in": |
| assert spec.oeb, "Internal error" |
| |
| # 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 |
| toppin_bb = _frm.toppins[pin_name] |
| oeb_bb = _frm.toppins[oeb_name] |
| out_bb = _frm.toppins[out_name] |
| is_leftrow = toppin_bb.center.x < _frm.boundary.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 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 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", |
| ) |
| via2sig_m3bb = via2_lay.bounds(mask=m3.mask) |
| elif spec.io_type == "io_out": |
| assert not spec.oeb, "Internal error" |
| |
| # 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 |
| sig_net = pin_net = ckt.new_net( |
| name=pin_name, external=True, childports=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] |
| |
| is_leftrow = toppin_bb.center.x < _frm.boundary.center.x |
| |
| # 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 |
| layouter.add_wire(net=sig_net, wire=m3, pin=m3pin, shape=toppin_bb) |
| if is_leftrow: |
| via2sig_m3bb = _geo.Rect( |
| left=toppin_bb.right, bottom=toppin_bb.bottom, |
| right=(toppin_bb.right + 2.0), top=toppin_bb.top, |
| ) |
| else: |
| via2sig_m3bb = _geo.Rect( |
| left=(toppin_bb.left - 2.0), bottom=toppin_bb.bottom, |
| right=toppin_bb.left, top=toppin_bb.top, |
| ) |
| |
| # 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) |
| else: |
| assert False, "Internal error" |
| |
| return sig_net, via2sig_m3bb |
| |
| # |
| # SP connections |
| # |
| |
| left_col = 1 |
| right_col = 1 |
| col_width = 10.0 |
| col_pitch = 20.0 |
| col_left0 = 20.0 |
| col_right0 = 2890.0 |
| |
| # Connect in_shiftclk |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["in_shiftclk"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.right - _viasig_m1bb.right |
| y = sramsig_m1pinbb.center.y |
| layouter.place(_viasig_lay, x=x, y=y) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_left = col_left0 + (left_col - 1)*col_pitch |
| col_right = col_left + col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (sig_m3bb.left, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.top + 1.0), |
| (col_left, sig_m3bb.top + 1.0), |
| (col_left, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.bottom), |
| (col_right, via2_m3bb.bottom), |
| (col_right, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| left_col += 1 |
| |
| # Connect shift_in |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["shift_in"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.right - _viasig_m1bb.right |
| y = sramsig_m1pinbb.center.y |
| layouter.place(_viasig_lay, x=x, y=y) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_left = col_left0 + (left_col - 1)*col_pitch |
| col_right = col_left + col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (sig_m3bb.left, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.top + 1.0), |
| (col_left, sig_m3bb.top + 1.0), |
| (col_left, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.bottom), |
| (col_right, via2_m3bb.bottom), |
| (col_right, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| left_col += 1 |
| |
| # Connect in_captureclk_l |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["in_captureclk_l"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.right - _viasig_m1bb.right |
| y = sramsig_m1pinbb.center.y |
| layouter.place(_viasig_lay, x=x, y=y) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_left = col_left0 + (left_col - 1)*col_pitch |
| col_right = col_left + col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (sig_m3bb.left, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.top + 1.0), |
| (col_left, sig_m3bb.top + 1.0), |
| (col_left, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.bottom), |
| (col_right, via2_m3bb.bottom), |
| (col_right, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| left_col += 1 |
| |
| # Connect in_captureclk |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["in_captureclk"], sram=spsram, |
| ) |
| |
| sramsig_m2pinbb = spsram_lay.bounds(mask=m2pin.mask, net=sig_net, depth=1) |
| |
| _via2sig_lay = layouter.wire_layout( |
| net=sig_net, wire=via2, rows=10, columns=10, |
| ) |
| _via2sig_m2bb = _via2sig_lay.bounds(mask=m2.mask) |
| |
| x = sramsig_m2pinbb.right - _via2sig_m2bb.right |
| y = sramsig_m2pinbb.top - _via2sig_m2bb.top |
| via2_lay = layouter.place(_via2sig_lay, x=x, y=y) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_left = col_left0 + (left_col - 1)*col_pitch |
| col_right = col_left + col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (sig_m3bb.left, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.top + 1.0), |
| (col_left, sig_m3bb.top + 1.0), |
| (col_left, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.bottom), |
| (col_right, via2_m3bb.bottom), |
| (col_right, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| left_col += 1 |
| |
| # Connect sramclk |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["sramclk"], sram=spsram, |
| ) |
| |
| sramsig_m2pinbb = spsram_lay.bounds(mask=m2pin.mask, net=sig_net, depth=1) |
| |
| _via2sig_lay = layouter.wire_layout( |
| net=sig_net, wire=via2, rows=10, columns=10, |
| ) |
| _via2sig_m2bb = _via2sig_lay.bounds(mask=m2.mask) |
| |
| x = sramsig_m2pinbb.right - _via2sig_m2bb.right |
| y = sramsig_m2pinbb.bottom - _via2sig_m2bb.bottom |
| via2_lay = layouter.place(_via2sig_lay, x=x, y=y) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_left = col_left0 + (left_col - 1)*col_pitch |
| col_right = col_left + col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (sig_m3bb.left, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.top + 1.0), |
| (col_left, sig_m3bb.top + 1.0), |
| (col_left, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.top), |
| (via2_m3bb.right, via2_m3bb.bottom), |
| (col_right, via2_m3bb.bottom), |
| (col_right, sig_m3bb.bottom), |
| (sig_m3bb.left, sig_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| left_col += 1 |
| |
| # Connect out_captureclk_l |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["out_captureclk_l"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.right - _viasig_m1bb.right |
| y = sramsig_m1pinbb.center.y |
| layouter.place(_viasig_lay, x=x, y=y) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_right = col_right0 - (right_col - 1)*col_pitch |
| col_left = col_right - col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (via2_m3bb.left, via2_m3bb.bottom), |
| (via2_m3bb.left, via2_m3bb.top), |
| (col_right, via2_m3bb.top), |
| (col_right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.bottom), |
| (col_left, sig_m3bb.bottom), |
| (col_left, via2_m3bb.bottom), |
| (via2_m3bb.left, via2_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| right_col += 1 |
| |
| # Connect out_captureclk |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["out_captureclk"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.left - _viasig_m1bb.left |
| y = sramsig_m1pinbb.center.y |
| layouter.place(_viasig_lay, x=x, y=y) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| col_right = col_right0 - (right_col - 1)*col_pitch |
| col_left = col_right - col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (via2_m3bb.left, via2_m3bb.bottom), |
| (via2_m3bb.left, via2_m3bb.top), |
| (col_right, via2_m3bb.top), |
| (col_right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.bottom), |
| (col_left, sig_m3bb.bottom), |
| (col_left, via2_m3bb.bottom), |
| (via2_m3bb.left, via2_m3bb.bottom), |
| )) |
| layouter.add_wire(net=sig_net, wire=m3, shape=shape) |
| |
| right_col += 1 |
| |
| # Connect out_docapture |
| sig_net, sig_m3bb = place_ioperiph( |
| spec=io_spsig2spec["out_docapture"], sram=spsram, |
| ) |
| |
| sramsig_m1pinbb = spsram_lay.bounds(mask=m1pin.mask, net=sig_net, depth=1) |
| |
| _viasig_lay = layouter.wire_layout( |
| net=sig_net, wire=via, rows=10, columns=10, |
| ) |
| _viasig_m1bb = _viasig_lay.bounds(mask=m1.mask) |
| |
| x = sramsig_m1pinbb.right + 5.0 - _viasig_m1bb.left |
| y = sramsig_m1pinbb.center.y - 1.0 |
| via_lay = layouter.place(_viasig_lay, x=x, y=y) |
| via_m1bb = via_lay.bounds(mask=m1.mask) |
| via2_lay = layouter.add_wire( |
| net=sig_net, wire=via2, rows=10, columns=10, x=x, y=y, |
| ) |
| via2_m3bb = via2_lay.bounds(mask=m3.mask) |
| |
| shape =_geo.Rect.from_rect(rect=sramsig_m1pinbb, right=via_m1bb.right) |
| layouter.add_wire(net=sig_net, wire=m1, shape=shape) |
| |
| col_right = col_right0 - (right_col - 1)*col_pitch |
| col_left = col_right - col_width |
| shape = _geo.Polygon.from_floats(points=( |
| (via2_m3bb.left, via2_m3bb.bottom), |
| (via2_m3bb.left, via2_m3bb.top), |
| (col_right, via2_m3bb.top), |
| (col_right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.top + 1.0), |
| (sig_m3bb.right, sig_m3bb.bottom), |
| (col_left, sig_m3bb.bottom), |
| (col_left, via2_m3bb.bottom), |
| (via2_m3bb |