Coverage for klayout_pex/rcx25/c/device_capacitor_plates.py: 100%

25 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-06 13:14 +0000

1# 

2# -------------------------------------------------------------------------------- 

3# SPDX-FileCopyrightText: 2024-2026 Martin Jan Köhler and Harald Pretl 

4# Johannes Kepler University, Institute for Integrated Circuits. 

5# 

6# This file is part of KPEX 

7# (see https://github.com/iic-jku/klayout-pex). 

8# 

9# This program is free software: you can redistribute it and/or modify 

10# it under the terms of the GNU General Public License as published by 

11# the Free Software Foundation, either version 3 of the License, or 

12# (at your option) any later version. 

13# 

14# This program is distributed in the hope that it will be useful, 

15# but WITHOUT ANY WARRANTY; without even the implied warranty of 

16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

17# GNU General Public License for more details. 

18# 

19# You should have received a copy of the GNU General Public License 

20# along with this program. If not, see <http://www.gnu.org/licenses/>. 

21# SPDX-License-Identifier: GPL-3.0-or-later 

22# -------------------------------------------------------------------------------- 

23# 

24from __future__ import annotations 

25 

26from dataclasses import dataclass, field 

27from typing import * 

28 

29import klayout.db as kdb 

30 

31from klayout_pex.klayout.lvsdb_extractor import DEVICE_CAPACITOR_PLATE_PROPERTY 

32from klayout_pex.types import NetName 

33 

34Shape = Union[kdb.PolygonWithProperties, kdb.EdgeWithProperties] 

35 

36 

37@dataclass 

38class DeviceCapacitorPlates: 

39 """ 

40 The plates of the black-boxed capacitor devices, whose models have the capacitances between them 

41 (the shapes of KIND_DEVICE_CAPACITOR_PLATE, e.g. the fingers of a MOM cap), 

42 but not their capacitances to other nets (e.g. the substrate, a wire across), which are extracted. 

43 Unless the substrate is a terminal of the device: then its model has them too (e.g. a VPP cap). 

44 

45 NOTE: a pair of plates is of a device if its nets are the terminal nets of a device, 

46 and not e.g. a wire of another net across the device, which is on its plate layers too 

47 """ 

48 

49 # the nets of the terminals of each device 

50 terminal_net_pairs: Set[FrozenSet[NetName]] = field(default_factory=set) 

51 

52 @classmethod 

53 def from_circuit(cls, 

54 circuit: kdb.Circuit, 

55 device_class_names: Set[str], 

56 substrate_net_name: NetName, 

57 substrate_shapes_net_name: NetName) -> DeviceCapacitorPlates: 

58 """ 

59 :param device_class_names: the device classes of the capacitors (e.g. the metal capacitors) 

60 :param substrate_net_name: the substrate net of the LVS netlist (e.g. sky130_gnd) 

61 :param substrate_shapes_net_name: the net of the substrate shapes of the extraction (e.g. VSUBS), 

62 which are a plate of the devices with a terminal on the substrate net 

63 """ 

64 terminal_net_pairs: Set[FrozenSet[NetName]] = set() 

65 for d in circuit.each_device(): 

66 if d.device_class().name not in device_class_names: 

67 continue 

68 net_names = sorted({net.expanded_name() 

69 for td in d.device_class().terminal_definitions() 

70 if (net := d.net_for_terminal(td.id())) is not None}) 

71 net_pairs = {frozenset((n1, n2)) for idx, n1 in enumerate(net_names) for n2 in net_names[idx + 1:]} 

72 terminal_net_pairs.update(net_pairs) 

73 terminal_net_pairs.update(frozenset(substrate_shapes_net_name if n == substrate_net_name else n 

74 for n in net_pair) 

75 for net_pair in net_pairs if substrate_net_name in net_pair) 

76 return DeviceCapacitorPlates(terminal_net_pairs=terminal_net_pairs) 

77 

78 def are_plates_of_a_device(self, shape1: Shape, net1: NetName, shape2: Shape, net2: NetName) -> bool: 

79 """ 

80 :return: whether the shapes are plates of a device between their nets, 

81 whose model has the capacitance between them 

82 """ 

83 return bool(shape1.property(DEVICE_CAPACITOR_PLATE_PROPERTY)) and \ 

84 bool(shape2.property(DEVICE_CAPACITOR_PLATE_PROPERTY)) and \ 

85 frozenset((net1, net2)) in self.terminal_net_pairs 

86 

87 def is_plate_edge_of_a_device(self, edge: kdb.EdgeWithProperties, net1: NetName, net2: NetName) -> bool: 

88 """ 

89 :return: whether the edge is one of a plate of a device between the nets, 

90 whose model has the fringe from it to the other plate, also where that is metal beyond the device 

91 (e.g. the perimeter capacitance of a MIM cap, whose bottom plate encloses the top plate) 

92 """ 

93 return bool(edge.property(DEVICE_CAPACITOR_PLATE_PROPERTY)) and \ 

94 frozenset((net1, net2)) in self.terminal_net_pairs