Coverage for klayout_pex/pex25d/show.py: 89%

92 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-09-17 19:08 +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# 

24 

25""" 

26Human-readable summary of a PEX25D artifact. 

27 

28Not a stable output. Anything that wants to consume the contents should use 

29``pex25d convert --out_format textpb`` and read the protobuf text format, which 

30is stable by definition. 

31""" 

32 

33from __future__ import annotations 

34 

35from typing import * 

36 

37from ..log import info, subproc, rule 

38 

39from .artifact import ArtifactKind 

40 

41 

42ALL_SECTIONS = ['header', 'meta', 'layers', 'dielectrics', 

43 'conductors', 'terminals', 'resistance', 'domain'] 

44 

45 

46def show(message: Any, 

47 kind: ArtifactKind, 

48 sections: Sequence[str] = ('all',)) -> None: 

49 selected = ALL_SECTIONS if 'all' in sections else [s for s in ALL_SECTIONS if s in sections] 

50 

51 is_scene = kind == ArtifactKind.SCENE 

52 

53 for section in selected: 

54 renderer = _RENDERERS.get(section) 

55 if renderer is None: 

56 continue 

57 renderer(message, is_scene) 

58 

59 

60def _grid_str(units: Any, value: int) -> str: 

61 """Render a grid-unit integer back into the declared LENGTH unit, for reading only.""" 

62 try: 

63 return f"{value * units.grid_numerator / units.grid_denominator:g}" 

64 except (AttributeError, ZeroDivisionError): 

65 return f"{value} grid" 

66 

67 

68def _show_header(message: Any, is_scene: bool) -> None: 

69 rule('Header') 

70 suffix = f"-{message.format_version_suffix}" if message.format_version_suffix else '' 

71 info(f"PEX25D {message.format_version_major}.{message.format_version_minor}{suffix} " 

72 f"({'scene' if is_scene else 'file'})") 

73 if message.HasField('units'): 

74 u = message.units 

75 subproc(f"units: {u}".replace('\n', ' ')) 

76 

77 

78def _show_meta(message: Any, is_scene: bool) -> None: 

79 metas = getattr(message, 'meta', None) 

80 if not metas: 

81 return 

82 rule('Metadata') 

83 for m in metas: 

84 subproc(f"{m.key} = {m.value}") 

85 

86 

87def _show_layers(message: Any, is_scene: bool) -> None: 

88 rule('Ground plane and layers') 

89 if message.HasField('ground_plane'): 

90 gp = message.ground_plane 

91 subproc(f"GROUND_PLANE {gp.name} z {gp.zlow}{gp.zhigh}") 

92 

93 if is_scene: 

94 for layer in message.layers: 

95 extra = '' 

96 if layer.connects_below or layer.connects_above: 

97 extra = f" connects {layer.connects_below}{layer.connects_above}" 

98 subproc(f"{layer.name} z {layer.zlow}{layer.zhigh}{extra}") 

99 else: 

100 for metal in message.metals: 

101 subproc(f"METAL {metal.name} z {metal.zlow}{metal.zhigh}") 

102 for via in message.vias: 

103 subproc(f"VIA {via.name} connects {via.connects_below}{via.connects_above}") 

104 

105 

106def _show_dielectrics(message: Any, is_scene: bool) -> None: 

107 rule('Dielectrics') 

108 for d in message.dielectrics: 

109 depth = f" depth {d.wrap_depth}" if is_scene else '' 

110 wraps = f" wraps {d.wraps}" if d.wraps else '' 

111 subproc(f"{d.name} k={d.permittivity}{wraps}{depth}") 

112 if message.HasField('background'): 

113 b = message.background 

114 subproc(f"{b.name} k={b.permittivity} (background)") 

115 

116 

117def _show_conductors(message: Any, is_scene: bool) -> None: 

118 rule('Conductors') 

119 for c in message.conductors: 

120 floating = ' FLOATING' if getattr(c, 'floating', False) else '' 

121 subproc(f"{c.name} net {c.net}{floating}") 

122 if not is_scene: 

123 info(f"{len(message.shapes)} shape record(s)") 

124 

125 

126def _show_terminals(message: Any, is_scene: bool) -> None: 

127 terminals = getattr(message, 'terminals', None) 

128 if not terminals: 

129 return 

130 rule('Terminals') 

131 for t in terminals: 

132 subproc(f"{t.name} layer {t.layer}") 

133 

134 

135def _show_resistance(message: Any, is_scene: bool) -> None: 

136 if not message.HasField('resistance_temperature'): 

137 return 

138 rule('Resistance') 

139 subproc(f"temperature: {message.resistance_temperature}".replace('\n', ' ')) 

140 for r in getattr(message, 'metal_resistances', []): 

141 subproc(f"metal {r.metal}: {r}".replace('\n', ' ')) 

142 for r in getattr(message, 'via_resistances', []): 

143 subproc(f"via {r.via}: {r}".replace('\n', ' ')) 

144 

145 

146def _show_domain(message: Any, is_scene: bool) -> None: 

147 if is_scene: 

148 if not message.HasField('domain'): 

149 info("No computational domain (the resolver never invents one)") 

150 return 

151 rule('Computational domain') 

152 subproc(str(message.domain).replace('\n', ' ')) 

153 else: 

154 which = message.WhichOneof('domain') 

155 if which is None: 

156 return 

157 rule('Computational domain') 

158 subproc(f"{which}: {getattr(message, which)}".replace('\n', ' ')) 

159 

160 

161_RENDERERS: Dict[str, Callable[[Any, bool], None]] = { 

162 'header': _show_header, 

163 'meta': _show_meta, 

164 'layers': _show_layers, 

165 'dielectrics': _show_dielectrics, 

166 'conductors': _show_conductors, 

167 'terminals': _show_terminals, 

168 'resistance': _show_resistance, 

169 'domain': _show_domain, 

170}