Coverage for tests/pex25d/pex25d_fixtures.py: 96%

26 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""" 

26A small but complete PEX25D file, and the helpers the tests read it with. 

27 

28The fixture is deliberately hand-written rather than generated from a PDK: it 

29has to stay readable, and a test that fails should point at a record somebody 

30can see, not at the output of the builder it is meant to be independent of. 

31""" 

32 

33from __future__ import annotations 

34 

35from typing import * 

36 

37from klayout_pex.pex25d.diagnostics import DiagnosticsReport 

38from klayout_pex.pex25d.reader import ReadError, read_pex25d_text 

39 

40HEADER = """\ 

41PEX25D 1.0-rc1 

42UNITS LENGTH um GRID 0.0001 

43META technology testpdk 

44META source_cell tiny 

45""" 

46 

47STACK = """\ 

48GROUND_PLANE subs Z_OFFSETS -0.4 -0.1 

49 

50METAL met1 Z_OFFSETS 1.0 1.4 

51METAL met2 Z_OFFSETS 2.0 2.4 

52VIA via1 CONNECTS met1 met2 

53 

54DIELECTRIC_BACKGROUND air PERMITTIVITY 1.0 

55DIELECTRIC_SIMPLE fox WRAPS subs PERMITTIVITY 3.9 BETWEEN subs met1 

56DIELECTRIC_CONFORMAL lint WRAPS met1 PERMITTIVITY 7.3 THICKNESS_OVER_WRAPPED 0.1 \\ 

57 THICKNESS_BESIDE_WRAPPED 0.08 THICKNESS_ON_FIELD 0.0 

58DIELECTRIC_SIMPLE ild WRAPS lint PERMITTIVITY 4.1 BETWEEN met1 met2 

59""" 

60 

61RESISTANCE = """\ 

62RESISTANCE TEMPERATURE 25.0 

63RESISTANCE METAL met1 SHEET 0.125 

64RESISTANCE METAL met2 SHEET 0.125 

65RESISTANCE VIA via1 PER_CUT 4.5 

66""" 

67 

68SHAPES = """\ 

69CONDUCTOR A neta 

70CONDUCTOR B netb 

71BOX CONDUCTOR A LAYER met1 LL 0.0 0.0 UR 1.0 0.5 

72BOX CONDUCTOR A LAYER via1 LL 0.2 0.1 UR 0.4 0.3 

73BOX CONDUCTOR A LAYER met2 LL 0.0 0.0 UR 1.0 0.5 

74POLYGON CONDUCTOR B LAYER met1 OUTER 2.0 0.0 3.0 0.0 3.0 1.0 2.0 1.0 

75TERMINAL t1 CONDUCTOR A LAYER met1 KIND PIN LL 0.0 0.0 UR 0.2 0.5 

76DOMAIN_MARGIN X 4.0 Y 4.0 Z 2.0 

77""" 

78 

79MINIMAL = f"{HEADER}\n{STACK}\n{RESISTANCE}\n{SHAPES}" 

80 

81# Everything except the conductors, for tests that supply their own geometry. 

82STACK_ONLY = f"{HEADER}\n{STACK}\n" 

83 

84 

85def read(text: str, 

86 report: Optional[DiagnosticsReport] = None, 

87 **kwargs: Any) -> Any: 

88 """Read PEX25D text, raising ``ReadError`` on a syntax error as usual.""" 

89 return read_pex25d_text(text.encode(), '<fixture>', report=report, **kwargs) 

90 

91 

92def read_codes(text: str) -> List[str]: 

93 """The diagnostic codes reading ``text`` produces, error or not.""" 

94 report = DiagnosticsReport() 

95 try: 

96 read(text, report=report) 

97 except ReadError: 

98 pass 

99 return codes(report) 

100 

101 

102def codes(report: DiagnosticsReport) -> List[str]: 

103 return [d.code for d in report.diagnostics] 

104 

105 

106def replacing(old: str, new: str, text: str = MINIMAL) -> str: 

107 """The fixture with one line or clause substituted. Fails loudly on a typo.""" 

108 assert text.count(old) == 1, f"{old!r} appears {text.count(old)} times" 

109 return text.replace(old, new) 

110 

111 

112def without(line: str, text: str = MINIMAL) -> str: 

113 return replacing(f"{line}\n", '', text)