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