/ test / unit / io / spice / pyspice.py
pyspice.py
  1  # SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-or-later OR CERN-OHL-S-2.0+ OR Apache-2.0
  2  # type: ignore
  3  import unittest
  4  from typing import cast
  5  
  6  from pdkmaster.technology import primitive as _prm
  7  from pdkmaster.design import cell as _cell
  8  
  9  from pdkmaster.io import spice as _sp
 10  
 11  from ...dummy import dummy_tech, dummy_cktfab, dummy_layoutfab, dummy_prims_spiceparams
 12  dummy_prims = dummy_tech.primitives
 13  
 14  class PySpiceFactoryTest(unittest.TestCase):
 15      def test_error(self):
 16          fab = _sp.PySpiceFactory(
 17              libfile="test.lib", corners=("fast", "typ", "slow"), conflicts={
 18                  "fast": ("typ", "slow"),
 19                  "typ": ("fast", "slow"),
 20                  "slow": ("fast", "typ"),
 21              },
 22              prims_params=dummy_prims_spiceparams,
 23          )
 24  
 25          #----
 26          # instance ports on net checking
 27          ckt = dummy_cktfab.new_circuit(name="test")
 28  
 29          res = ckt.instantiate(
 30              cast(_prm.Resistor, dummy_prims.resistor), name="res",
 31              width=1.0, height=3.0,
 32          )
 33          ckt.new_net(name="res_p1", external=True, childports=res.ports.port1)
 34  
 35          # res.ports.port2 not on a net
 36          with self.assertRaises(ValueError):
 37              fab.new_pyspicesubcircuit(circuit=ckt)
 38  
 39          ckt.new_net(name="res_p1b", external=True, childports=res.ports.port1)
 40  
 41          # res.ports.port1 on two nets
 42          with self.assertRaises(ValueError):
 43              fab.new_pyspicesubcircuit(circuit=ckt)
 44  
 45          #----
 46          # wrong corner spec
 47          ckt = dummy_cktfab.new_circuit(name="test")
 48  
 49          # wrong corner
 50          with self.assertRaises(ValueError):
 51              fab.new_pyspicecircuit(corner="error", top=ckt)
 52          # conflicting corner
 53          with self.assertRaises(ValueError):
 54              fab.new_pyspicecircuit(corner=("slow", "fast"), top=ckt)
 55  
 56          #----
 57          # MIMCapacitor in lvs
 58          ckt = dummy_cktfab.new_circuit(name="test")
 59  
 60          mim = ckt.instantiate(
 61              cast(_prm.MIMCapacitor, dummy_prims.MIMCap), name="mim",
 62              width=0.5, height=0.5,
 63          )
 64          ckt.new_net(name="mim_bot", external=True, childports=mim.ports.bottom)
 65          ckt.new_net(name="mim_top", external=True, childports=mim.ports.top)
 66  
 67          with self.assertRaises(NotImplementedError):
 68              fab.new_pyspicesubcircuit(circuit=ckt, lvs=True)
 69  
 70          #----
 71          # MIMCapacitor not a subcircuit model
 72          params = _sp.SpicePrimsParamSpec()
 73          params.add_device_params(
 74              prim=cast(_prm.MIMCapacitor, dummy_prims.MIMCap), is_subcircuit=False,
 75          )
 76          fab = _sp.PySpiceFactory(
 77              libfile="test.lib", corners=("typ",), conflicts={}, prims_params=params,
 78          )
 79  
 80          ckt = dummy_cktfab.new_circuit(name="test")
 81  
 82          mim = ckt.instantiate(
 83              cast(_prm.MIMCapacitor, dummy_prims.MIMCap), name="mim",
 84              width=0.5, height=0.5,
 85          )
 86          ckt.new_net(name="mim_bot", external=True, childports=mim.ports.bottom)
 87          ckt.new_net(name="mim_top", external=True, childports=mim.ports.top)
 88  
 89          with self.assertRaises(NotImplementedError):
 90              fab.new_pyspicesubcircuit(circuit=ckt)
 91  
 92      def test_pyspicecircuit(self):
 93          # Wrong corner in conflicts
 94          with self.assertRaises(ValueError):
 95              _sp.PySpiceFactory(
 96                  libfile="test.lib", corners=("typ",), conflicts={"error": "error2"},
 97                  prims_params=dummy_prims_spiceparams,
 98              )
 99  
100          # Currently only run some code for code coverage.
101          # No checking done ATM.
102          fab = _sp.PySpiceFactory(
103              libfile="test.lib", corners=("typ",), conflicts={},
104              prims_params=dummy_prims_spiceparams,
105          )
106  
107          mos_cell = _cell.Cell(
108              name="mytest",
109              tech=dummy_tech, cktfab=dummy_cktfab, layoutfab=dummy_layoutfab,
110          )
111          mos_ckt = mos_cell.new_circuit()
112          mos = mos_ckt.instantiate(cast(_prm.MOSFET, dummy_prims.nmos), name="mos")
113          mos_ckt.new_net(name="s", external=True, childports=mos.ports.sourcedrain1)
114          mos_ckt.new_net(name="g", external=True, childports=mos.ports.gate)
115          mos_ckt.new_net(name="d", external=True, childports=mos.ports.sourcedrain2)
116          mos_ckt.new_net(name="b", external=True, childports=mos.ports.bulk)
117  
118  
119          ckt = dummy_cktfab.new_circuit(name="test")
120  
121          res = ckt.instantiate(
122              cast(_prm.Resistor, dummy_prims.resistor), name="res",
123              width=1.0, height=3.0,
124          )
125          ckt.new_net(name="res_p1", external=True, childports=res.ports.port1)
126          ckt.new_net(name="res_p2", external=True, childports=res.ports.port2)
127  
128          res2 = ckt.instantiate(
129              cast(_prm.Resistor, dummy_prims.metal2res), name="res2",
130          )
131          ckt.new_net(name="res2_p1", external=True, childports=res2.ports.port1)
132          ckt.new_net(name="res2_p2", external=True, childports=res2.ports.port2)
133  
134          ndio = ckt.instantiate(
135              cast(_prm.Diode, dummy_prims.ndiode), name="ndio",
136          )
137          ckt.new_net(name="ndio_ano", external=True, childports=ndio.ports.anode)
138          ckt.new_net(name="ndio_cath", external=True, childports=ndio.ports.cathode)
139  
140          pdio = ckt.instantiate(
141              cast(_prm.Diode, dummy_prims.pdiode), name="pdio",
142          )
143          ckt.new_net(name="pdio_ano", external=True, childports=pdio.ports.anode)
144          ckt.new_net(name="pdio_cath", external=True, childports=pdio.ports.cathode)
145  
146          nmos = ckt.instantiate(mos_cell, name="nmos")
147          ckt.new_net(name="nmos_s", external=True, childports=nmos.ports.s)
148          ckt.new_net(name="nmos_g", external=True, childports=nmos.ports.g)
149          ckt.new_net(name="nmos_d", external=True, childports=nmos.ports.d)
150          ckt.new_net(name="nmos_b", external=True, childports=nmos.ports.b)
151  
152          pmos = ckt.instantiate(
153              cast(_prm.MOSFET, dummy_prims.pmos), name="pmos",
154          )
155          ckt.new_net(name="pmos_s", external=True, childports=pmos.ports.sourcedrain1)
156          ckt.new_net(name="pmos_g", external=True, childports=pmos.ports.gate)
157          ckt.new_net(name="pmos_d", external=True, childports=pmos.ports.sourcedrain2)
158          ckt.new_net(name="pmos_b", external=True, childports=pmos.ports.bulk)
159  
160          mim = ckt.instantiate(
161              cast(_prm.MIMCapacitor, dummy_prims.MIMCap), name="mim",
162              width=0.5, height=0.5,
163          )
164          ckt.new_net(name="mim_bot", external=True, childports=mim.ports.bottom)
165          ckt.new_net(name="mim_top", external=True, childports=mim.ports.top)
166  
167          mim2 = ckt.instantiate(
168              cast(_prm.MIMCapacitor, dummy_prims.MIMCap2), name="mim2",
169          )
170          ckt.new_net(name="mim2_bot", external=True, childports=mim2.ports.bottom)
171          ckt.new_net(name="mim2_top", external=True, childports=mim2.ports.top)
172  
173          npn = ckt.instantiate(cast(_prm.Bipolar, dummy_prims.npn), name="npn")
174          ckt.new_net(name="npn_c", external=True, childports=npn.ports.collector)
175          ckt.new_net(name="npn_b", external=True, childports=npn.ports.base)
176          ckt.new_net(name="npn_e", external=True, childports=npn.ports.emitter)
177  
178          pnp = ckt.instantiate(cast(_prm.Bipolar, dummy_prims.pnp), name="pnp")
179          ckt.new_net(name="pnp_c", external=True, childports=pnp.ports.collector)
180          ckt.new_net(name="pnp_b", external=True, childports=pnp.ports.base)
181          ckt.new_net(name="pnp_e", external=True, childports=pnp.ports.emitter)
182  
183          fab.new_pyspicecircuit(corner="typ", top=ckt)
184  
185  
186          ckt = dummy_cktfab.new_circuit(name="test")
187  
188          res = ckt.instantiate(
189              cast(_prm.Resistor, dummy_prims.metal2res), name="res",
190              width=1.0, height=3.0,
191          )
192          ckt.new_net(name="res_p1", external=True, childports=res.ports.port1)
193          ckt.new_net(name="res_p2", external=True, childports=res.ports.port2)
194  
195          fab.new_pyspicesubcircuit(circuit=ckt, lvs=True)