lambdapdk 0.1.37__py3-none-any.whl → 0.1.39__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lambdapdk/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import siliconcompiler.package as sc_package
2
2
 
3
3
 
4
- __version__ = "0.1.37"
4
+ __version__ = "0.1.39"
5
5
 
6
6
 
7
7
  def register_data_source(chip):
@@ -20,10 +20,10 @@ def get_pdks():
20
20
  Returns a list of pdk names in lambdapdk
21
21
  '''
22
22
 
23
- from lambdapdk import asap7, freepdk45, sky130, gf180, ihp130
23
+ from lambdapdk import asap7, freepdk45, sky130, gf180, ihp130, interposer
24
24
 
25
25
  all_pdks = []
26
- for pdk_mod in [asap7, freepdk45, sky130, gf180, ihp130]:
26
+ for pdk_mod in [asap7, freepdk45, sky130, gf180, ihp130, interposer]:
27
27
  pdks = pdk_mod.setup()
28
28
  if not isinstance(pdks, (list, tuple)):
29
29
  pdks = [pdks]
@@ -43,6 +43,7 @@ def get_libs():
43
43
  from lambdapdk.sky130.libs import sky130sc, sky130io, sky130sram
44
44
  from lambdapdk.gf180.libs import gf180mcu, gf180io, gf180sram
45
45
  from lambdapdk.ihp130.libs import sg13g2_stdcell, sg13g2_sram
46
+ from lambdapdk.interposer.libs import bumps as interposer_bumps
46
47
 
47
48
  all_libs = []
48
49
  for lib_mod in [
@@ -50,7 +51,8 @@ def get_libs():
50
51
  nangate45, fakeram45,
51
52
  sky130sc, sky130io, sky130sram,
52
53
  gf180mcu, gf180io, gf180sram,
53
- sg13g2_stdcell, sg13g2_sram]:
54
+ sg13g2_stdcell, sg13g2_sram,
55
+ interposer_bumps]:
54
56
  libs = lib_mod.setup()
55
57
  if not isinstance(libs, (list, tuple)):
56
58
  libs = [libs]
@@ -111,6 +111,10 @@ def setup():
111
111
  pdk.set('pdk', process, 'pexmodel', 'openroad-openrcx', stackup, 'typical',
112
112
  pdkdir + '/pex/openroad/typical.rules')
113
113
 
114
+ # Relaxed routing rules
115
+ pdk.set('pdk', process, 'file', 'openroad', 'relax_routing_rules', stackup,
116
+ pdkdir + '/apr/openroad_relaxed_rules.tcl')
117
+
114
118
  # Hide the DIEAREA layer 235/*.
115
119
  pdk.add('pdk', process, 'var', 'klayout', 'hide_layers', stackup, '235/0')
116
120
  pdk.add('pdk', process, 'var', 'klayout', 'hide_layers', stackup, '235/5')
@@ -46,16 +46,13 @@ def _setup_lib(libname, suffix):
46
46
  # site name
47
47
  lib.set('asic', 'site', libtype, 'asap7sc7p5t')
48
48
 
49
- # clock buffers
50
- lib.add('asic', 'cells', 'clkbuf', f"BUFx2_ASAP7_75t_{suffix}")
49
+ # clock buffers - remove once openroad driver supports it
50
+ lib.add('asic', 'cells', 'clkbuf', f"BUFx4_ASAP7_75t_{suffix}")
51
51
 
52
52
  # tie cells
53
53
  lib.add('asic', 'cells', 'tie', [f"TIEHIx1_ASAP7_75t_{suffix}",
54
54
  f"TIELOx1_ASAP7_75t_{suffix}"])
55
55
 
56
- # hold cells
57
- lib.add('asic', 'cells', 'hold', f"BUFx2_ASAP7_75t_{suffix}")
58
-
59
56
  # filler
60
57
  lib.add('asic', 'cells', 'filler', [f"FILLER_ASAP7_75t_{suffix}",
61
58
  f"FILLERxp5_ASAP7_75t_{suffix}"])
@@ -92,7 +89,6 @@ def _setup_lib(libname, suffix):
92
89
  lib.set('option', 'var', 'openroad_macro_place_halo', ['10', '10'])
93
90
  lib.set('option', 'var', 'openroad_macro_place_channel', ['12', '12'])
94
91
 
95
- lib.set('option', 'var', 'openroad_cts_clock_buffer', f"BUFx4_ASAP7_75t_{suffix}")
96
92
  lib.set('option', 'var', 'openroad_cts_distance_between_buffers', "60")
97
93
 
98
94
  lib.set('option', 'var', 'yosys_abc_clock_multiplier', "1") # convert from ps -> ps
@@ -45,15 +45,14 @@ def setup():
45
45
  lib.add('output', stackup, 'cdl', libdir + '/cdl/NangateOpenCellLibrary.cdl')
46
46
 
47
47
  # clock buffers
48
- lib.add('asic', 'cells', 'clkbuf', "BUF_X4")
48
+ lib.set('asic', 'cells', 'clkbuf', ["CLKBUF_X1",
49
+ "CLKBUF_X2",
50
+ "CLKBUF_X3"])
49
51
 
50
52
  # tie cells
51
53
  lib.add('asic', 'cells', 'tie', ["LOGIC1_X1",
52
54
  "LOGIC0_X1"])
53
55
 
54
- # hold cells
55
- lib.add('asic', 'cells', 'hold', "BUF_X1")
56
-
57
56
  # filler
58
57
  lib.add('asic', 'cells', 'filler', ["FILLCELL_X1",
59
58
  "FILLCELL_X2",
@@ -108,8 +108,6 @@ def setup():
108
108
  lib.set('option', 'file', 'openroad_tapcells',
109
109
  libdir + '/apr/openroad/tapcell.tcl')
110
110
 
111
- lib.set('option', 'var', 'openroad_cts_clock_buffer',
112
- f"gf180mcu_fd_sc_mcu{libtype}5v0__clkbuf_8")
113
111
  lib.set('option', 'var', 'openroad_cts_distance_between_buffers', "100")
114
112
 
115
113
  lib.set('option', 'var', 'yosys_abc_clock_multiplier', "1000") # convert from ns -> ps
@@ -15,7 +15,7 @@ def setup():
15
15
  path_base = 'ihp-sg13g2/libs.ref/sg13g2_sram'
16
16
  lib.add('output', stackup, 'lef', f'{path_base}/lef/{mem_name}.lef')
17
17
  lib.add('output', stackup, 'gds', f'{path_base}/gds/{mem_name}.gds')
18
- lib.add('output', stackup, 'cdl', f'{path_base}/spice/{mem_name}.cdl')
18
+ lib.add('output', stackup, 'cdl', f'{path_base}/cdl/{mem_name}.cdl')
19
19
 
20
20
  lib.add('output', 'typ', 'nldm', f'{path_base}/lib/{mem_name}_typ_1p20V_25C.lib')
21
21
  lib.add('output', 'slow', 'nldm', f'{path_base}/lib/{mem_name}_slow_1p08V_125C.lib')
@@ -56,7 +56,7 @@ def setup():
56
56
  lib.add('output', 'rtl', 'verilog',
57
57
  'ihp-sg13g2/libs.ref/sg13g2_stdcell/verilog/sg13g2_stdcell.v')
58
58
 
59
- # clock buffers
59
+ # clock buffers - remove once openroad driver supports it
60
60
  lib.add('asic', 'cells', 'clkbuf', ["sg13g2_buf_2",
61
61
  "sg13g2_buf_4"])
62
62
 
@@ -65,8 +65,9 @@ def setup():
65
65
  "LOGIC0_X1"])
66
66
 
67
67
  # hold cells
68
- lib.add('asic', 'cells', 'hold', ["sg13g2_buf_1",
69
- "sg13g2_buf_4"])
68
+ lib.add('asic', 'cells', 'hold', ["sg13g2_dlygate4sd1_1",
69
+ "sg13g2_dlygate4sd2_1",
70
+ "sg13g2_dlygate4sd3_1"])
70
71
 
71
72
  # filler
72
73
  lib.add('asic', 'cells', 'filler', ["sg13g2_fill_1",
@@ -81,9 +82,8 @@ def setup():
81
82
  # antenna
82
83
  lib.add('asic', 'cells', 'antenna', ["sg13g2_antennanp"])
83
84
 
84
- # Stupid small cells
85
- lib.add('asic', 'cells', 'dontuse', ["sg13g2_antennanp",
86
- "sg13g2_lgcp_1",
85
+ # Dont use
86
+ lib.add('asic', 'cells', 'dontuse', ["sg13g2_lgcp_1",
87
87
  "sg13g2_sighold",
88
88
  "sg13g2_slgcp_1",
89
89
  "sg13g2_dfrbp_2"])
@@ -0,0 +1,111 @@
1
+ import os
2
+ import siliconcompiler
3
+ from lambdapdk import register_data_source
4
+
5
+
6
+ stackups = []
7
+ for m in ("3ML", "4ML", "5ML"):
8
+ for w in ("0400", "0800", "2000", "0400_2000"):
9
+ stackups.append(f'{m}_{w}')
10
+
11
+
12
+ ####################################################
13
+ # PDK Setup
14
+ ####################################################
15
+ def setup():
16
+ '''
17
+ The interposer PDK is a passive technology with a number of
18
+ simulated stackups. The PDK contains enablement for place and
19
+ route tools and design rule signoff.
20
+ Note that this process design kit is provided as an academic
21
+ and research aid only and the resulting designs are not manufacturable.
22
+ '''
23
+
24
+ foundry = 'virtual'
25
+ process = 'interposer'
26
+
27
+ libtype = 'none'
28
+
29
+ node = 130
30
+ # TODO: dummy numbers, only matter for cost estimation
31
+ wafersize = 300
32
+ hscribe = 0.1
33
+ vscribe = 0.1
34
+ edgemargin = 2
35
+
36
+ pdkdir = os.path.join('lambdapdk', 'interposer', 'base')
37
+
38
+ pdk = siliconcompiler.PDK(process, package='lambdapdk')
39
+ register_data_source(pdk)
40
+
41
+ # process name
42
+ pdk.set('pdk', process, 'foundry', foundry)
43
+ pdk.set('pdk', process, 'node', node)
44
+ pdk.set('pdk', process, 'version', 'v0.0.1')
45
+ pdk.set('pdk', process, 'stackup', stackups)
46
+ pdk.set('pdk', process, 'wafersize', wafersize)
47
+ pdk.set('pdk', process, 'edgemargin', edgemargin)
48
+ pdk.set('pdk', process, 'scribe', (hscribe, vscribe))
49
+
50
+ # APR Setup
51
+ for stackup in stackups:
52
+ for tool in ('openroad', 'klayout', 'magic'):
53
+ pdk.set('pdk', process, 'aprtech', tool, stackup, libtype, 'lef',
54
+ pdkdir + f'/apr/{stackup}.lef')
55
+
56
+ pdk.set('pdk', process, 'minlayer', stackup, 'metal1')
57
+ pdk.set('pdk', process, 'maxlayer', stackup, 'topmetal')
58
+
59
+ # DRC Runsets
60
+ pdk.set('pdk', process, 'drc', 'runset', 'klayout', stackup, 'drc',
61
+ pdkdir + f'/setup/klayout/{stackup}.drc')
62
+
63
+ key = 'drc_params:drc'
64
+ pdk.add('pdk', process, 'var', 'klayout', stackup, key, 'input=<input>')
65
+ pdk.add('pdk', process, 'var', 'klayout', stackup, key, 'topcell=<topcell>')
66
+ pdk.add('pdk', process, 'var', 'klayout', stackup, key, 'report=<report>')
67
+ pdk.add('pdk', process, 'var', 'klayout', stackup, key, 'threads=<threads>')
68
+
69
+ # Layer map and display file
70
+ pdk.set('pdk', process, 'layermap', 'klayout', 'def', 'gds', stackup,
71
+ pdkdir + f'/apr/{stackup}.layermap')
72
+ pdk.set('pdk', process, 'display', 'klayout', stackup,
73
+ pdkdir + f'/setup/klayout/{stackup}.lyp')
74
+
75
+ pdk.set('pdk', process, 'aprtech', 'openroad', stackup, libtype, 'fill',
76
+ pdkdir + f'/dfm/openroad/{stackup}.fill.json')
77
+
78
+ # Openroad global routing grid derating
79
+ openroad_layer_adjustments = {
80
+ 'metal1': 0.20,
81
+ 'metal2': 0.20,
82
+ 'metal3': 0.20,
83
+ 'metal4': 0.20,
84
+ 'metal5': 0.20,
85
+ 'metal6': 0.20,
86
+ 'topmetal': 0.20
87
+ }
88
+ for layer, adj in openroad_layer_adjustments.items():
89
+ if layer != 'topmetal' and int(layer[-1]) >= int(stackup[0]):
90
+ continue
91
+ pdk.set('pdk', process, 'var', 'openroad', f'{layer}_adjustment', stackup, adj)
92
+
93
+ pdk.set('pdk', process, 'var', 'openroad', 'rclayer_signal', stackup, 'metal2')
94
+ pdk.set('pdk', process, 'var', 'openroad', 'rclayer_clock', stackup, 'metal2')
95
+
96
+ pdk.set('pdk', process, 'var', 'openroad', 'pin_layer_vertical', stackup, 'metal2')
97
+ pdk.set('pdk', process, 'var', 'openroad', 'pin_layer_horizontal', stackup, 'metal3')
98
+
99
+ # PEX
100
+ for corner in ["minimum", "typical", "maximum"]:
101
+ pdk.set('pdk', process, 'pexmodel', 'openroad', stackup, corner,
102
+ pdkdir + '/pex/openroad/' + stackup + '.' + corner + '.tcl')
103
+
104
+ return pdk
105
+
106
+
107
+ #########################
108
+ if __name__ == "__main__":
109
+ pdk = setup()
110
+ pdk.write_manifest(f'{pdk.top()}.json')
111
+ pdk.check_filepaths()
@@ -0,0 +1,533 @@
1
+ import json
2
+ import os
3
+
4
+ from jinja2 import Environment, FileSystemLoader
5
+ import xml.etree.ElementTree as ET
6
+ import xml.dom.minidom
7
+
8
+ template_dir = os.path.join(os.path.dirname(__file__), 'base', 'templates')
9
+ jinja2_env = Environment(loader=FileSystemLoader(template_dir))
10
+
11
+
12
+ LICENSE = '''Copyright 2024 ZeroASIC Corp
13
+
14
+ Licensed under the Apache License, Version 2.0 (the "License");
15
+ you may not use this file except in compliance with the License.
16
+ You may obtain a copy of the License at
17
+
18
+ https://www.apache.org/licenses/LICENSE-2.0
19
+
20
+ Unless required by applicable law or agreed to in writing, software
21
+ distributed under the License is distributed on an "AS IS" BASIS,
22
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23
+ See the License for the specific language governing permissions and
24
+ limitations under the License.
25
+ '''
26
+
27
+
28
+ def __get_gds_type_name(gds_type):
29
+ if gds_type == 0:
30
+ return ""
31
+ if gds_type == 10:
32
+ return "pin"
33
+ if gds_type == 20:
34
+ return "text"
35
+ if gds_type == 30:
36
+ return "fill"
37
+
38
+ raise ValueError(str(gds_type))
39
+
40
+
41
+ def make_metal_layer(
42
+ name,
43
+ gds_mapping,
44
+ direction,
45
+ min_width,
46
+ min_spacing,
47
+ resistance_per_um,
48
+ capacitance_per_um,
49
+ max_width=None):
50
+
51
+ layer = {
52
+ "name": name,
53
+ "type": "ROUTING",
54
+ "direction": direction,
55
+ "gds": gds_mapping,
56
+ "width": {
57
+ "min": min_width
58
+ },
59
+ "spacing": {
60
+ "min": min_spacing
61
+ },
62
+ "parasitic": {
63
+ "resistance": resistance_per_um,
64
+ "capacitance": capacitance_per_um
65
+ }
66
+ }
67
+
68
+ if max_width:
69
+ layer["width"]["max"] = max_width
70
+
71
+ return layer
72
+
73
+
74
+ def make_cut_layer(
75
+ name,
76
+ gds_mapping,
77
+ width,
78
+ min_spacing,
79
+ enclosure_bot,
80
+ enclosure_top,
81
+ resistance_per_cut):
82
+
83
+ layer = {
84
+ "name": name,
85
+ "type": "CUT",
86
+ "gds": gds_mapping,
87
+ "width": {
88
+ "min": width
89
+ },
90
+ "spacing": {
91
+ "min": min_spacing
92
+ },
93
+ "enclosure": {
94
+ "bottom": enclosure_bot,
95
+ "top": enclosure_top
96
+ },
97
+ "parasitic": {
98
+ "resistance": resistance_per_cut
99
+ }
100
+ }
101
+
102
+ return layer
103
+
104
+
105
+ def build_tech(layer_count, name=None, width=None):
106
+ if not isinstance(width, (tuple, list)):
107
+ width = layer_count * [width]
108
+
109
+ layers = []
110
+ gds_layer = 1
111
+ for n in range(layer_count):
112
+ layeridx = n + 1
113
+
114
+ metal_name = f"metal{layeridx}"
115
+ max_width = 5.0
116
+ if layeridx == layer_count:
117
+ metal_name = "topmetal"
118
+ gds_layer = 100
119
+ max_width = None
120
+
121
+ layers.append(
122
+ make_metal_layer(
123
+ metal_name,
124
+ {
125
+ "number": gds_layer,
126
+ "types": {
127
+ "NET": 0,
128
+ "SPNET": 0,
129
+ "PIN": 10,
130
+ "LEFPIN": 10,
131
+ "FILL": 30
132
+ },
133
+ "name": {
134
+ "PIN": 20,
135
+ "SPNET": 20,
136
+ "TEXT": 20
137
+ }
138
+ },
139
+ "HORIZONTAL" if layeridx % 2 == 1 else "VERTICAL",
140
+ min_width=width[n],
141
+ min_spacing=width[n],
142
+ resistance_per_um=1.5000e-03,
143
+ capacitance_per_um=1.0000E-01,
144
+ max_width=max_width
145
+ ))
146
+
147
+ if layeridx != layer_count:
148
+ layers.append(
149
+ make_cut_layer(
150
+ f"via{layeridx}",
151
+ {
152
+ "number": gds_layer + 1,
153
+ "types": {
154
+ "NET": 0,
155
+ "SPNET": 0,
156
+ "PIN": 10,
157
+ "LEFPIN": 10,
158
+ "FILL": 30
159
+ },
160
+ "name": {
161
+ "PIN": 20,
162
+ "SPNET": 20,
163
+ "TEXT": 20
164
+ }
165
+ },
166
+ width[n] / 2,
167
+ width[n] / 2,
168
+ width[n] / 2,
169
+ width[n] / 2,
170
+ resistance_per_cut=10e-3
171
+ ))
172
+ gds_layer += 2
173
+
174
+ if not name:
175
+ name = f"{layer_count}ML"
176
+
177
+ tech = {
178
+ "name": name,
179
+ "grid": 0.005,
180
+ "layers": layers,
181
+ "outline": (0, 0)
182
+ }
183
+
184
+ return tech
185
+
186
+
187
+ def build_layermap(tech, path):
188
+ layermap = []
189
+ for layer in tech["layers"]:
190
+ name = layer["name"]
191
+ gds_number = layer["gds"]["number"]
192
+
193
+ for map_type, gds_type in layer["gds"]["types"].items():
194
+ layermap.append((
195
+ name,
196
+ map_type,
197
+ str(gds_number),
198
+ str(gds_type)
199
+ ))
200
+
201
+ for map_type, gds_type in layer["gds"]["name"].items():
202
+ layermap.append((
203
+ "NAME",
204
+ f"{name}/{map_type}",
205
+ str(gds_number),
206
+ str(gds_type)
207
+ ))
208
+
209
+ layermap.append((
210
+ "DIEAREA",
211
+ "ALL",
212
+ str(tech["outline"][0]),
213
+ str(tech["outline"][1])
214
+ ))
215
+
216
+ os.makedirs(path, exist_ok=True)
217
+ with open(f'{path}/{tech["name"]}.layermap', 'w') as f:
218
+ f.write(
219
+ jinja2_env.get_template('layermap.j2').render(
220
+ license=LICENSE,
221
+ layers=layermap
222
+ )
223
+ )
224
+ f.write('\n')
225
+
226
+
227
+ def build_openroad_pex(tech, path):
228
+ corners = {
229
+ "maximum": 0.3,
230
+ "typical": 0.0,
231
+ "minimum": -0.3
232
+ }
233
+
234
+ for corner, adjustment in corners.items():
235
+ metals = []
236
+ vias = []
237
+ for layer in tech["layers"]:
238
+ name = layer["name"]
239
+
240
+ if layer["type"] == "ROUTING":
241
+ metals.append((
242
+ name,
243
+ layer["parasitic"]["resistance"]*(1+adjustment),
244
+ layer["parasitic"]["capacitance"]*(1+adjustment)
245
+ ))
246
+ else:
247
+ vias.append((
248
+ name,
249
+ layer["parasitic"]["resistance"]*(1+adjustment)
250
+ ))
251
+
252
+ os.makedirs(path, exist_ok=True)
253
+ with open(f'{path}/{tech["name"]}.{corner}.tcl', 'w') as f:
254
+ f.write(
255
+ jinja2_env.get_template('pex.tcl.j2').render(
256
+ license=LICENSE,
257
+ metals=metals,
258
+ vias=vias
259
+ )
260
+ )
261
+ f.write('\n')
262
+
263
+
264
+ def build_klayout_drc(tech, path):
265
+ layers = []
266
+
267
+ for n, layer in enumerate(tech["layers"]):
268
+ layer_nm = {
269
+ "name": layer["name"],
270
+ "type": layer["type"],
271
+ "width": {
272
+ "min": int(layer["width"]["min"] * 1000)
273
+ },
274
+ "spacing": {
275
+ "min": int(layer["spacing"]["min"] * 1000)
276
+ }
277
+ }
278
+
279
+ if "max" in layer["width"]:
280
+ layer_nm["width"]["max"] = int(layer["width"]["max"] * 1000)
281
+
282
+ if layer["type"] == "CUT":
283
+ layer_nm["enclosure"] = {
284
+ "bottom": (
285
+ tech["layers"][n - 1]["name"],
286
+ int(layer["enclosure"]["bottom"] * 1000)
287
+ ),
288
+ "top": (
289
+ tech["layers"][n + 1]["name"],
290
+ int(layer["enclosure"]["top"] * 1000)
291
+ )
292
+ }
293
+
294
+ gds_types = sorted(set(layer["gds"]["types"].values()))
295
+ layer_nm["gds"] = [
296
+ (layer["gds"]["number"], gds_type) for gds_type in gds_types
297
+ ]
298
+
299
+ layers.append(layer_nm)
300
+
301
+ os.makedirs(path, exist_ok=True)
302
+ with open(f'{path}/{tech["name"]}.drc', 'w') as f:
303
+ f.write(
304
+ jinja2_env.get_template('drc.j2').render(
305
+ license=LICENSE,
306
+ grid=int(tech["grid"] * 1000),
307
+ layers=layers,
308
+ outline={"number": tech["outline"][0], "type": tech["outline"][1]}
309
+ )
310
+ )
311
+ f.write('\n')
312
+
313
+
314
+ def build_klayout_layer_properties(tech, path):
315
+ colors = [
316
+ "#ffc280",
317
+ "#ff9d9d",
318
+ "#ff80a8",
319
+ "#c080ff",
320
+ "#9580ff",
321
+ "#8086ff",
322
+ "#80a8ff",
323
+ "#ff0000",
324
+ "#ff0080",
325
+ "#ff00ff",
326
+ "#8000ff",
327
+ "#91ff00",
328
+ "#008000",
329
+ "#508000",
330
+ "#808000",
331
+ "#805000"
332
+ ]
333
+ patterns = [
334
+ "I5",
335
+ "I9"
336
+ ]
337
+
338
+ layeridx = 0
339
+
340
+ def make_layer(name, gds):
341
+ prop = ET.Element("properties")
342
+
343
+ color = colors[layeridx % len(colors)]
344
+ pattern = patterns[layeridx % len(patterns)]
345
+
346
+ for tag, value in [
347
+ ("frame-color", color),
348
+ ("frame-brightness", "0"),
349
+ ("fill-color", color),
350
+ ("fill-brightness", "0"),
351
+ ("dither-pattern", pattern),
352
+ ("line-style", None),
353
+ ("value", "true"),
354
+ ("visible", "true"),
355
+ ("transparent", "false"),
356
+ ("width", "1"),
357
+ ("marked", "false"),
358
+ ("xfill", "false"),
359
+ ("animation", "0"),
360
+ ("name", name),
361
+ ("source", f"{name} {gds[0]}/{gds[1]}@1")
362
+ ]:
363
+ el = ET.Element(tag)
364
+
365
+ if value is not None:
366
+ el.text = value
367
+ prop.append(el)
368
+
369
+ return prop
370
+
371
+ props = ET.Element("layer-properties")
372
+ props.append(make_layer("outline", (tech["outline"][0], tech["outline"][1])))
373
+ layeridx += 1
374
+ for layer in tech["layers"]:
375
+ gds_types = sorted(set([*layer["gds"]["types"].values(), *layer["gds"]["name"].values()]))
376
+ for gds_type in gds_types:
377
+ name = layer["name"]
378
+ gds_name = __get_gds_type_name(gds_type)
379
+ if gds_type:
380
+ name += "." + gds_name
381
+ props.append(make_layer(name, (layer["gds"]["number"], gds_type)))
382
+
383
+ layeridx += 1
384
+
385
+ props.insert(0, ET.Comment(f"\n{LICENSE}"))
386
+
387
+ os.makedirs(path, exist_ok=True)
388
+ with open(f'{path}/{tech["name"]}.lyp', 'w') as f:
389
+ f.write(
390
+ xml.dom.minidom.parseString(
391
+ ET.tostring(props)).toprettyxml(indent=" "))
392
+
393
+
394
+ def build_lef(tech, path):
395
+ vias = []
396
+ for n, layer in enumerate(tech["layers"]):
397
+ if layer["type"] == "ROUTING":
398
+ continue
399
+ bottom_layer = tech["layers"][n - 1]
400
+ top_layer = tech["layers"][n + 1]
401
+
402
+ name = layer["name"].upper() + "_1"
403
+
404
+ cut_egde = layer["width"]["min"]
405
+ cut = (-cut_egde / 2, -cut_egde / 2, cut_egde / 2, cut_egde / 2)
406
+ bot = (cut[0] - layer["enclosure"]['bottom'], cut[1] - layer["enclosure"]['bottom'],
407
+ cut[2] + layer["enclosure"]['bottom'], cut[3] + layer["enclosure"]['bottom'])
408
+ top = (cut[0] - layer["enclosure"]['top'], cut[1] - layer["enclosure"]['top'],
409
+ cut[2] + layer["enclosure"]['top'], cut[3] + layer["enclosure"]['top'])
410
+
411
+ vias.append({
412
+ "name": name,
413
+ "layers": [
414
+ (layer["name"], [f"{v:.3f}" for v in cut]),
415
+ (bottom_layer["name"], [f"{v:.3f}" for v in bot]),
416
+ (top_layer["name"], [f"{v:.3f}" for v in top])
417
+ ]})
418
+
419
+ os.makedirs(path, exist_ok=True)
420
+ with open(f'{path}/{tech["name"]}.lef', 'w') as f:
421
+ f.write(
422
+ jinja2_env.get_template('lef.j2').render(
423
+ license=LICENSE,
424
+ grid=tech["grid"],
425
+ layers=tech["layers"],
426
+ vias=vias
427
+ )
428
+ )
429
+
430
+
431
+ def build_openroad_fill(tech, path):
432
+ fill = {"layers": {}}
433
+
434
+ for layer in tech["layers"]:
435
+ if layer["type"] == "CUT":
436
+ continue
437
+
438
+ max_spacing = 5 * layer["spacing"]["min"]
439
+
440
+ max_width = 10 * layer["width"]["min"]
441
+ if "max" in layer["width"]:
442
+ max_width = layer["width"]["max"]
443
+
444
+ shapes = []
445
+ for ratio in (1.0, 0.75, 0.5, 0.25, 0.0):
446
+ width = layer["width"]["min"] + ratio * (max_width - layer["width"]["min"])
447
+ width = tech["grid"] * round(width / tech["grid"])
448
+
449
+ shapes.append(width)
450
+
451
+ fill["layers"][layer["name"]] = {
452
+ "name": layer["name"],
453
+ "layer": layer["gds"]["number"],
454
+ "datatype": layer["gds"]["types"]["NET"],
455
+ "space_to_outline": max_spacing,
456
+ "non-opc": {
457
+ "datatype": layer["gds"]["types"]["FILL"],
458
+ "width": shapes,
459
+ "height": shapes,
460
+ "space_to_fill": layer["spacing"]["min"],
461
+ "space_to_non_fill": max_spacing
462
+ }
463
+ }
464
+
465
+ os.makedirs(path, exist_ok=True)
466
+ with open(f'{path}/{tech["name"]}.fill.json', 'w') as f:
467
+ json.dump(fill, f, indent=2)
468
+
469
+
470
+ def build_readme(stackups):
471
+ from lambdapdk import interposer
472
+
473
+ for stackup in stackups:
474
+ metals = []
475
+ for layer in stackup["tech"]["layers"]:
476
+ if layer["type"] == "ROUTING":
477
+ metals.append((
478
+ layer["name"],
479
+ f'{int(layer["width"]["min"] * 1000)}nm',
480
+ f'{int(layer["spacing"]["min"] * 1000)}nm'
481
+ ))
482
+ stackup["metalstack"] = reversed(metals)
483
+
484
+ with open('README.md', 'w') as f:
485
+ f.write(
486
+ jinja2_env.get_template('README.md.j2').render(
487
+ desc=interposer.setup.__doc__,
488
+ license=LICENSE,
489
+ stackups=stackups
490
+ )
491
+ )
492
+
493
+
494
+ if __name__ == "__main__":
495
+ def tapered_metal(layer_count):
496
+ mid = int(layer_count // 2)
497
+ widths = mid * [0.4]
498
+ if layer_count % 2 != 0:
499
+ widths += [0.8]
500
+ widths += mid * [2.0]
501
+ return widths
502
+
503
+ tech_specs = {
504
+ "0400": 0.4,
505
+ "0800": 0.8,
506
+ "2000": 2.0,
507
+ "0400_2000": tapered_metal
508
+ }
509
+
510
+ stackups = []
511
+
512
+ for name, width in tech_specs.items():
513
+ for layer_count in range(3, 6):
514
+ tech_name = f"{layer_count}ML_{name}"
515
+
516
+ widths = width
517
+ if not isinstance(widths, (int, float, list, tuple)):
518
+ widths = widths(layer_count)
519
+
520
+ tech = build_tech(layer_count, name=tech_name, width=widths)
521
+ stackups.append({
522
+ "name": tech_name,
523
+ "tech": tech
524
+ })
525
+
526
+ build_lef(tech, 'base/apr')
527
+ build_layermap(tech, 'base/apr')
528
+ build_klayout_layer_properties(tech, 'base/setup/klayout')
529
+ build_klayout_drc(tech, 'base/setup/klayout')
530
+ build_openroad_pex(tech, 'base/pex/openroad')
531
+ build_openroad_fill(tech, 'base/dfm/openroad')
532
+
533
+ build_readme(stackups)
@@ -0,0 +1,31 @@
1
+ import os
2
+ import siliconcompiler
3
+ from lambdapdk import register_data_source
4
+ from lambdapdk.interposer import stackups
5
+
6
+
7
+ def setup():
8
+ '''
9
+ Interposer bump library
10
+ '''
11
+ libdir = "lambdapdk/interposer/libs/bumps/"
12
+
13
+ lib = siliconcompiler.Library('interposer_bumps', package='lambdapdk')
14
+ register_data_source(lib)
15
+
16
+ # pdk
17
+ lib.set('option', 'pdk', 'interposer')
18
+
19
+ for stackup in stackups:
20
+ lib.set('output', stackup, 'lef',
21
+ os.path.join(libdir, 'lef/bumps.lef'))
22
+ lib.add('output', stackup, 'gds',
23
+ os.path.join(libdir, 'gds/bumps.gds'))
24
+
25
+ return lib
26
+
27
+
28
+ #########################
29
+ if __name__ == "__main__":
30
+ lib = setup(siliconcompiler.Chip('<lib>'))
31
+ lib.write_manifest(f'{lib.top()}.json')
@@ -104,19 +104,20 @@ def setup():
104
104
  lib.add('asic', 'cells', 'clkbuf', [f'sky130_fd_sc_{libtype}__clkbuf_1',
105
105
  f'sky130_fd_sc_{libtype}__clkbuf_2',
106
106
  f'sky130_fd_sc_{libtype}__clkbuf_4',
107
- f'sky130_fd_sc_{libtype}__clkbuf_6',
108
107
  f'sky130_fd_sc_{libtype}__clkbuf_8',
109
- f'sky130_fd_sc_{libtype}__clkbuf_12',
110
108
  f'sky130_fd_sc_{libtype}__clkbuf_16'])
109
+ if libtype == "hdll":
110
+ lib.add('asic', 'cells', 'clkbuf', [f'sky130_fd_sc_{libtype}__clkbuf_6',
111
+ f'sky130_fd_sc_{libtype}__clkbuf_12'])
111
112
 
112
113
  # hold cells
113
- lib.add('asic', 'cells', 'hold', [f'sky130_fd_sc_{libtype}__buf_1',
114
- f'sky130_fd_sc_{libtype}__buf_2',
115
- f'sky130_fd_sc_{libtype}__buf_4',
116
- f'sky130_fd_sc_{libtype}__buf_6',
117
- f'sky130_fd_sc_{libtype}__buf_8',
118
- f'sky130_fd_sc_{libtype}__buf_12',
119
- f'sky130_fd_sc_{libtype}__buf_16'])
114
+ lib.add('asic', 'cells', 'hold', [f'sky130_fd_sc_{libtype}__dlygate4sd1_1',
115
+ f'sky130_fd_sc_{libtype}__dlygate4sd2_1',
116
+ f'sky130_fd_sc_{libtype}__dlygate4sd3_1'])
117
+ if libtype == "hd":
118
+ lib.add('asic', 'cells', 'hold', [f'sky130_fd_sc_{libtype}__dlymetal6s2s_1',
119
+ f'sky130_fd_sc_{libtype}__dlymetal6s4s_1',
120
+ f'sky130_fd_sc_{libtype}__dlymetal6s6s_1'])
120
121
 
121
122
  # filler
122
123
  lib.add('asic', 'cells', 'filler', [f'sky130_fd_sc_{libtype}__fill_1',
@@ -160,8 +161,6 @@ def setup():
160
161
  lib.set('option', 'file', 'openroad_tapcells',
161
162
  libdir + '/apr/openroad/tapcell.tcl')
162
163
 
163
- lib.set('option', 'var', 'openroad_cts_clock_buffer', f"sky130_fd_sc_{libtype}__clkbuf_4")
164
-
165
164
  lib.set('option', 'var', 'yosys_abc_clock_multiplier', "1000") # convert from ns -> ps
166
165
 
167
166
  cap_table = {
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lambdapdk
3
- Version: 0.1.37
3
+ Version: 0.1.39
4
4
  Summary: Library of open source Process Design Kits
5
5
  Author: Zero ASIC
6
- License: Apache License
6
+ License: Apache License
7
7
  Version 2.0, January 2004
8
8
  http://www.apache.org/licenses/
9
9
 
@@ -198,14 +198,15 @@ Project-URL: Homepage, https://github.com/siliconcompiler/lambdapdk
198
198
  Requires-Python: >=3.8
199
199
  Description-Content-Type: text/markdown
200
200
  License-File: LICENSE
201
- Requires-Dist: siliconcompiler >=0.27.0
201
+ Requires-Dist: siliconcompiler>=0.27.0
202
202
  Provides-Extra: test
203
- Requires-Dist: flake8 ==7.1.1 ; extra == 'test'
204
- Requires-Dist: pytest ==8.3.3 ; extra == 'test'
205
- Requires-Dist: pytest-timeout ==2.3.1 ; extra == 'test'
206
- Requires-Dist: tclint ==0.4.2 ; extra == 'test'
207
- Requires-Dist: lambdalib ==0.3.0 ; extra == 'test'
208
- Requires-Dist: sc-leflib ==0.4.0 ; extra == 'test'
203
+ Requires-Dist: flake8==7.1.1; extra == "test"
204
+ Requires-Dist: pytest==8.3.4; extra == "test"
205
+ Requires-Dist: pytest-timeout==2.3.1; extra == "test"
206
+ Requires-Dist: tclint==0.5.0; extra == "test"
207
+ Requires-Dist: lambdalib==0.3.2; extra == "test"
208
+ Requires-Dist: sc-leflib==0.4.0; extra == "test"
209
+ Requires-Dist: Jinja2==3.1.4; extra == "test"
209
210
 
210
211
  # Lambdapdk Introduction
211
212
 
@@ -221,6 +222,7 @@ Supported PDKs:
221
222
  * [Skywater130](lambdapdk/sky130/base/README.md)
222
223
  * [Global Foundries 180](lambdapdk/gf180/README.md)
223
224
  * [IHP 180](https://github.com/IHP-GmbH/IHP-Open-PDK)
225
+ * [interposer](lambdapdk/interposer/README.md)
224
226
 
225
227
  # License
226
228
 
@@ -0,0 +1,27 @@
1
+ lambdapdk/__init__.py,sha256=Ub57VU23usHGYzywXktgCbnaRoi8-HQUBwdyfDlkzfI,1771
2
+ lambdapdk/asap7/__init__.py,sha256=RrRTx_dFEsHnTWCGwzNUVmY8-2lOAd2EcWs7w8KwRPI,4736
3
+ lambdapdk/asap7/libs/asap7sc7p5t.py,sha256=YCj1yCYLylF72EMJx2zY9KX1ZIkGsHuMDsCGrq9ud4E,6030
4
+ lambdapdk/asap7/libs/fakeio7.py,sha256=t4OQ7uljg4jh14bf3o4wsnI5gNkLEVpMs1drispc7LE,978
5
+ lambdapdk/asap7/libs/fakeram7.py,sha256=thOHy0XyV2-pakoxrttGL3qU9C2OeL1-MVtlrSiTMu8,1112
6
+ lambdapdk/freepdk45/__init__.py,sha256=5lbmMi3tGM1mJoM1wOGG0GhjKaFc2Qi3x-tRO8kKSDs,3673
7
+ lambdapdk/freepdk45/libs/fakeram45.py,sha256=CqX9YTFLQFlmGn4IZDjEI1OXv43mg3aWdbVfMrBKNSU,1091
8
+ lambdapdk/freepdk45/libs/nangate45.py,sha256=vh_OsCzTKInpAVt1oC6QOyPjEuxFzYRGY3Ngopffh-o,4638
9
+ lambdapdk/gf180/__init__.py,sha256=fbJ4z0kXBftIHAsgyqJmbpZfpWf9PWuxXgoQoG3IhQ0,7963
10
+ lambdapdk/gf180/libs/gf180io.py,sha256=6WgsALOaKWYG6RfNFz6sg36zmfZZFl2HnsDWOWYy1UM,2751
11
+ lambdapdk/gf180/libs/gf180mcu.py,sha256=g8s6xpH8Glj6m7aIubX2EmyG2eLXbVsZOSWKYhaujQ8,6604
12
+ lambdapdk/gf180/libs/gf180sram.py,sha256=uNtf4JOaT6wlD7D6hN5XwCoIOPmAXkVxSJXQuqNdY6k,2140
13
+ lambdapdk/ihp130/__init__.py,sha256=dSXwH5rYOrJc9C3FTe9bxCJw9VZaK0fnRBMWaWT9NY8,5500
14
+ lambdapdk/ihp130/libs/sg13g2_sram.py,sha256=oFg89-kKvuBBI9IsdGkuTsG9Q8i4gzImF66MXMXY4Fk,1885
15
+ lambdapdk/ihp130/libs/sg13g2_stdcell.py,sha256=un7-NQBTYCMWJOjLMgw1BcBdy1kjEX_kjoRY1QpDmXI,5134
16
+ lambdapdk/interposer/__init__.py,sha256=UEAxcCLo-0OheGNCOTLpwnF05HFAz3agStFBTdcgS58,4039
17
+ lambdapdk/interposer/_generator.py,sha256=Uf9i7gfjgntUpLmxsRmOfzfgh26iQ3nfZmrdIx59UtA,15016
18
+ lambdapdk/interposer/libs/bumps.py,sha256=-b3cNOUBKhzzxoWI1Y047g9Ev3a_mXBMLKO3BaHo1sI,771
19
+ lambdapdk/sky130/__init__.py,sha256=PXJEa-siz7HStlvDTCI794evU8qbHl472gHckSrZFWc,4124
20
+ lambdapdk/sky130/libs/sky130io.py,sha256=wFmN9RC98f6OFM5E5acCUgPe35tVTKciJzIcUBlmumo,2029
21
+ lambdapdk/sky130/libs/sky130sc.py,sha256=GSgYjyPQPFzEKByP2P9VJVOIr1hqMe_glow6TSszIx4,9399
22
+ lambdapdk/sky130/libs/sky130sram.py,sha256=ILjAjbgDdvfiWqmjmj8mmHvU7YiYGZVHGO7jmyajnaY,1489
23
+ lambdapdk-0.1.39.dist-info/LICENSE,sha256=2TIhku7H905BsYloYoCwat2JsdkGYc_qsnvU-p7P-IQ,10766
24
+ lambdapdk-0.1.39.dist-info/METADATA,sha256=d2bajU8KNRguQHB5k40TTeM8APUAc9igQeLVHx25F1o,13947
25
+ lambdapdk-0.1.39.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
26
+ lambdapdk-0.1.39.dist-info/top_level.txt,sha256=5wk8psZwCcQgSjOlWTihBeDkSIViGn8I3j5yALbs59s,10
27
+ lambdapdk-0.1.39.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,24 +0,0 @@
1
- lambdapdk/__init__.py,sha256=P2gEp_UXfnGsDxtCuMoUtIVg9WZPgxAVz7FoInH56pY,1649
2
- lambdapdk/asap7/__init__.py,sha256=Tb3du7LEisL_YoXVzVZgy9NqC835F79a9zqlV0MwBO0,4571
3
- lambdapdk/asap7/libs/asap7sc7p5t.py,sha256=mKymeYPmNTHgxfwyIShxUN5hO1iAzgx2MWoBbfPkChU,6159
4
- lambdapdk/asap7/libs/fakeio7.py,sha256=t4OQ7uljg4jh14bf3o4wsnI5gNkLEVpMs1drispc7LE,978
5
- lambdapdk/asap7/libs/fakeram7.py,sha256=thOHy0XyV2-pakoxrttGL3qU9C2OeL1-MVtlrSiTMu8,1112
6
- lambdapdk/freepdk45/__init__.py,sha256=5lbmMi3tGM1mJoM1wOGG0GhjKaFc2Qi3x-tRO8kKSDs,3673
7
- lambdapdk/freepdk45/libs/fakeram45.py,sha256=CqX9YTFLQFlmGn4IZDjEI1OXv43mg3aWdbVfMrBKNSU,1091
8
- lambdapdk/freepdk45/libs/nangate45.py,sha256=z319EBburWngwe_WYMHfksiFvfgJ0h4zLCEeWkcHTVs,4592
9
- lambdapdk/gf180/__init__.py,sha256=fbJ4z0kXBftIHAsgyqJmbpZfpWf9PWuxXgoQoG3IhQ0,7963
10
- lambdapdk/gf180/libs/gf180io.py,sha256=6WgsALOaKWYG6RfNFz6sg36zmfZZFl2HnsDWOWYy1UM,2751
11
- lambdapdk/gf180/libs/gf180mcu.py,sha256=NPwcviynLMXRlkvAdwNrp8sJxHzT78PE0Iwy-DNpgzA,6727
12
- lambdapdk/gf180/libs/gf180sram.py,sha256=uNtf4JOaT6wlD7D6hN5XwCoIOPmAXkVxSJXQuqNdY6k,2140
13
- lambdapdk/ihp130/__init__.py,sha256=dSXwH5rYOrJc9C3FTe9bxCJw9VZaK0fnRBMWaWT9NY8,5500
14
- lambdapdk/ihp130/libs/sg13g2_sram.py,sha256=3D5MKweUeOpWTHmtESbt8AyddjAvcpw9SeSbz4cfFG4,1887
15
- lambdapdk/ihp130/libs/sg13g2_stdcell.py,sha256=i92vJvo2bur2lL9YKtyrZ2kDP8QiqmbTqWnSQeSiKhE,5085
16
- lambdapdk/sky130/__init__.py,sha256=PXJEa-siz7HStlvDTCI794evU8qbHl472gHckSrZFWc,4124
17
- lambdapdk/sky130/libs/sky130io.py,sha256=wFmN9RC98f6OFM5E5acCUgPe35tVTKciJzIcUBlmumo,2029
18
- lambdapdk/sky130/libs/sky130sc.py,sha256=wW6sAaPpuqqCDqUD2cdJZJXqQz6KRLz9xztQ-hLNaC8,9446
19
- lambdapdk/sky130/libs/sky130sram.py,sha256=ILjAjbgDdvfiWqmjmj8mmHvU7YiYGZVHGO7jmyajnaY,1489
20
- lambdapdk-0.1.37.dist-info/LICENSE,sha256=2TIhku7H905BsYloYoCwat2JsdkGYc_qsnvU-p7P-IQ,10766
21
- lambdapdk-0.1.37.dist-info/METADATA,sha256=uZccfzu6ySZkmLcy3u4H-NKr5f70z4bdq4W2jqRRNyg,13834
22
- lambdapdk-0.1.37.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
23
- lambdapdk-0.1.37.dist-info/top_level.txt,sha256=5wk8psZwCcQgSjOlWTihBeDkSIViGn8I3j5yALbs59s,10
24
- lambdapdk-0.1.37.dist-info/RECORD,,