--- /dev/null
+/*.png
+/*.txt
--- /dev/null
+all: \
+net_gnd.png \
+net_vcc.png \
+metal_gnd.png \
+metal_vcc.png \
+#buried2.png \
+#diff2.png \
+#metal2.png \
+#pads2.png \
+#poly2.png \
+#vias2.png
+
+# GND is metal ID 0, corresponding net is found as follows:
+# grep ' 1 0$' nets.txt
+net_gnd.png: layers.txt nets.txt
+ ./layers_net.py $^ 17 $@
+
+# VCC is metal ID 1, corresponding net is found as follows:
+# grep ' 1 1$' nets.txt
+net_vcc.png: layers.txt nets.txt
+ ./layers_net.py $^ 21 $@
+
+nets.txt: layers.txt
+ ./nets.py <$< >$@
+
+layers.txt: \
+pads.txt \
+metal.txt \
+vias.txt \
+poly.txt \
+buried.txt \
+drain_source.txt \
+split_diff.txt
+ ./layers.py pads.txt,metal.txt,vias.txt,poly.txt,buried.txt,drain_source.txt,split_diff.txt 0:1,1:2,2:3,2:6,3:4,3:5,4:6,5:6 >$@
+
+metal_gnd.png: metal.txt
+ ./layer_id.py $< 0 $@
+
+metal_vcc.png: metal.txt
+ ./layer_id.py $< 1 $@
+
+drain_source.txt: drain_source.png
+ ./layer.py $< >$@
+
+split_diff.txt: split_diff.png
+ ./layer.py $< >$@
+
+buried.txt: buried.png
+ ./layer.py $< >$@
+
+metal.txt: metal.png
+ ./layer.py $< >$@
+
+pads.txt: pads.png
+ ./layer.py $< >$@
+
+poly.txt: poly.png
+ ./layer.py $< >$@
+
+vias.txt: vias.png
+ ./layer.py $< >$@
+
+drain_source.png: split_diff.png poly.png buried.png
+ ./boolean.py 'split_diff.png&poly.png&~+buried.png' $@
+
+split_diff.png: diff.png poly.png buried.png
+ ./boolean.py 'diff.png&~(-poly.png&~buried.png)' $@
+
+buried.png: orig/AMD_8085_Buried_orig_7267w.png
+ ./to_mono.py $< $@
+
+diff.png: orig/AMD_8085_Diffusion_orig_7267w.png
+ ./to_mono.py $< $@
+
+metal.png: orig/AMD_8085_Metal_orig_7267w.png
+ ./to_mono.py $< $@
+
+pads.png: orig/AMD_8085_Pads_orig_7267w.png
+ ./to_mono.py $< $@
+
+poly.png: orig/AMD_8085_Polysilicon_orig_7267w.png
+ ./to_mono.py $< $@
+
+vias.png: orig/AMD_8085_Vias_orig_7267w.png
+ ./to_mono.py $< $@
+
+buried1.png: orig/AMD_8085_Buried_7267w.png
+ ./to_mono.py $< $@
+
+diff1.png: orig/AMD_8085_Diffusion_7267w.png
+ ./to_mono.py $< $@
+
+metal1.png: orig/AMD_8085_Metal_7267w.png
+ ./to_mono.py $< $@
+
+pads1.png: orig/AMD_8085_Pads_7267w.png
+ ./to_mono.py $< $@
+
+poly1.png: orig/AMD_8085_Polysilicon_7267w.png
+ ./to_mono.py $< $@
+
+vias1.png: orig/AMD_8085_Vias_7267w.png
+ ./to_mono.py $< $@
+
+buried2.png: buried1.png
+ ./boolean.py 'buried.png^buried1.png' $@
+
+diff2.png: diff1.png
+ ./boolean.py 'diff.png^diff1.png' $@
+
+metal2.png: metal1.png
+ ./boolean.py 'metal.png^metal1.png' $@
+
+pads2.png: pads1.png
+ ./boolean.py 'pads.png^pads1.png' $@
+
+poly2.png: poly1.png
+ ./boolean.py 'poly.png^poly1.png' $@
+
+vias2.png: vias1.png
+ ./boolean.py 'vias.png^vias1.png' $@
+
+clean:
+ rm -f *.png *.txt
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+EPSILON = 1e-6
+
+PIL.Image.warnings.simplefilter('ignore', PIL.Image.DecompressionBombWarning)
+
+if len(sys.argv) < 3:
+ print(f'usage: {sys.argv[0]:s} expr image_out')
+ sys.exit(1)
+expr = sys.argv[1]
+image_out = sys.argv[2]
+
+ops = set(['~', '-', '+', '&', '^', '|', ')'])
+def expr0(i):
+ if i < len(expr) and expr[i] == '(':
+ i += 1
+ image, i = expr3(i)
+ assert expr[i] == ')'
+ i += 1
+ return image, i
+ if i < len(expr) and expr[i] == '~':
+ i += 1
+ image, i = expr0(i)
+ return numpy.logical_not(image), i
+ if i < len(expr) and expr[i] == '-':
+ # shrink by one pixel
+ i += 1
+ image, i = expr0(i)
+ image1 = numpy.ones_like(image)
+ image1[:-1, :-1] &= image[1:, 1:]
+ image1[:-1, :] &= image[1:, :]
+ image1[:-1, 1:] &= image[1:, :-1]
+ image1[:, :-1] &= image[:, 1:]
+ image1[:, :] &= image[:, :]
+ image1[:, 1:] &= image[:, :-1]
+ image1[1:, :-1] &= image[:-1, 1:]
+ image1[1:, :] &= image[:-1, :]
+ image1[1:, 1:] &= image[:-1, :-1]
+ return image1, i
+ if i < len(expr) and expr[i] == '+':
+ # expand by one pixel
+ i += 1
+ image, i = expr0(i)
+ image1 = numpy.zeros_like(image)
+ image1[:-1, :-1] |= image[1:, 1:]
+ image1[:-1, :] |= image[1:, :]
+ image1[:-1, 1:] |= image[1:, :-1]
+ image1[:, :-1] |= image[:, 1:]
+ image1[:, :] |= image[:, :]
+ image1[:, 1:] |= image[:, :-1]
+ image1[1:, :-1] |= image[:-1, 1:]
+ image1[1:, :] |= image[:-1, :]
+ image1[1:, 1:] |= image[:-1, :-1]
+ return image1, i
+ j = i
+ while j < len(expr) and expr[j] not in ops:
+ j += 1
+ return numpy.array(PIL.Image.open(expr[i:j])), j
+def expr1(i):
+ image, i = expr0(i)
+ while i < len(expr) and expr[i] == '&':
+ i += 1
+ image1, i = expr0(i)
+ image = numpy.logical_and(image, image1)
+ return image, i
+def expr2(i):
+ image, i = expr1(i)
+ while i < len(expr) and expr[i] == '^':
+ i += 1
+ image1, i = expr1(i)
+ image = numpy.logical_xor(image, image1)
+ return image, i
+def expr3(i):
+ image, i = expr2(i)
+ while i < len(expr) and expr[i] == '|':
+ i += 1
+ image1, i = expr2(i)
+ image = numpy.logical_or(image, image1)
+ return image, i
+image, i = expr3(0)
+assert i == len(expr)
+PIL.Image.fromarray(image).save(image_out)
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+PIL.Image.warnings.simplefilter('ignore', PIL.Image.DecompressionBombWarning)
+
+if len(sys.argv) < 2:
+ print(
+ f'usage: {sys.argv[0]:s} image_in'
+ )
+ sys.exit(1)
+image_in = sys.argv[1]
+
+image = PIL.Image.open(image_in)
+image.load()
+#print('mode', image.mode)
+#print('palette', image.palette)
+#print('width', image.width)
+#print('height', image.height)
+image = numpy.array(image)
+#print('shape', image.shape)
+#print('dtype', image.dtype)
+assert len(image.shape) == 2 and image.dtype == bool
+ys, xs = image.shape
+
+image[:, 1:] ^= image[:, :-1]
+nz_y, nz_x = numpy.nonzero(image)
+
+ptrs = []
+items = []
+for i in range(0, nz_y.shape[0], 2):
+ y = nz_y[i]
+ while len(ptrs) <= y:
+ ptrs.append(len(items))
+ items.append([len(items), y, nz_x[i], nz_x[i + 1]])
+while len(ptrs) <= ys:
+ ptrs.append(len(items))
+
+for i in range(ys - 1):
+ p = ptrs[i]
+ q = ptrs[i + 1]
+ r = ptrs[i + 2]
+ if q > p and r > q:
+ j = p
+ jid, _, jx0, jx1 = items[j]
+ k = q
+ kid, _, kx0, kx1 = items[k]
+ while True:
+ if kx0 >= jx1:
+ j += 1
+ if j >= q:
+ break
+ jid, _, jx0, jx1 = items[j]
+ elif jx0 >= kx1:
+ k += 1
+ if k >= r:
+ break
+ kid, _, kx0, kx1 = items[k]
+ else:
+ l = items[jid][0]
+ while l != jid:
+ jid = l
+ l = items[jid][0]
+
+ l = items[kid][0]
+ while l != kid:
+ kid = l
+ l = items[kid][0]
+
+ id = min(jid, kid)
+ items[jid][0] = id
+ items[kid][0] = id
+
+ jid = j
+ while jid != id:
+ l = items[jid][0]
+ items[jid][0] = id
+ jid = l
+
+ kid = k
+ while kid != id:
+ l = items[kid][0]
+ items[kid][0] = id
+ kid = l
+
+ if jx1 < kx1:
+ j += 1
+ if j >= q:
+ break
+ jid, _, jx0, jx1 = items[j]
+ else:
+ k += 1
+ if k >= r:
+ break
+ kid, _, kx0, kx1 = items[k]
+
+print(ys, xs)
+for i in range(len(items)):
+ id, y, x0, x1 = items[i]
+
+ l = items[id][0]
+ while l != id:
+ id = l
+ l = items[id][0]
+
+ l = i
+ while l != id:
+ m = items[l][0]
+ items[l][0] = id
+ l = m
+
+ print(id, y, x0, x1)
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+if len(sys.argv) < 4:
+ print(
+ f'usage: {sys.argv[0]:s} layer.txt id image_out'
+ )
+ sys.exit(1)
+layer_txt = sys.argv[1]
+id = int(sys.argv[2])
+image_out = sys.argv[3]
+
+with open(layer_txt) as fin:
+ line = fin.readline()
+ assert len(line)
+ ys, xs = [int(i) for i in line.split()]
+ image = numpy.zeros((ys, xs), bool)
+
+ line = fin.readline()
+ while len(line):
+ id1, y, x0, x1 = [int(i) for i in line.split()]
+ if id1 == id:
+ image[y, x0:x1] = True
+ line = fin.readline()
+PIL.Image.fromarray(image).save(image_out)
--- /dev/null
+#!/usr/bin/env python3
+
+import sys
+
+if len(sys.argv) < 3:
+ print(
+ f'usage: {sys.argv[0]:s} layer0.txt,layer1.txt,... l0:l1,l0:l2,...'
+ )
+ sys.exit(1)
+layers_in = sys.argv[1].split(',')
+layer_pairs = []
+for i in sys.argv[2].split(','):
+ l0, l1 = [int(j) for j in i.split(':')]
+ layer_pairs.append((l0, l1))
+
+layers = []
+for layer_in in layers_in:
+ with open(layer_in) as fin:
+ line = fin.readline()
+ assert len(line)
+ ys, xs = [int(i) for i in line.split()]
+
+ ptrs = []
+ items = []
+ line = fin.readline()
+ while len(line):
+ id, y, x0, x1 = [int(i) for i in line.split()]
+ while len(ptrs) <= y:
+ ptrs.append(len(items))
+ items.append((id, y, x0, x1))
+ line = fin.readline()
+ while len(ptrs) <= ys:
+ ptrs.append(len(items))
+ layers.append(((ys, xs), ptrs, items))
+(ys, xs) = layers[0][0]
+assert all([i[0] == (ys, xs) for i in layers])
+
+print(len(layers), ys, xs)
+for _, _, items in layers:
+ print(len(items))
+ for id, y, x0, x1 in items:
+ print(id, y, x0, x1)
+
+for l0, l1 in layer_pairs:
+ _, ptrs0, items0 = layers[l0]
+ _, ptrs1, items1 = layers[l1]
+
+ found = set()
+ for i in range(ys):
+ p = ptrs0[i]
+ q = ptrs0[i + 1]
+ r = ptrs1[i]
+ s = ptrs1[i + 1]
+ if q > p and s > r:
+ j = p
+ jid, _, jx0, jx1 = items0[j]
+ k = r
+ kid, _, kx0, kx1 = items1[k]
+ while True:
+ if kx0 >= jx1:
+ j += 1
+ if j >= q:
+ break
+ jid, _, jx0, jx1 = items0[j]
+ elif jx0 >= kx1:
+ k += 1
+ if k >= s:
+ break
+ kid, _, kx0, kx1 = items1[k]
+ else:
+ key = (jid, kid)
+ if key not in found:
+ found.add(key)
+ print(l0, l1, jid, kid)
+ if jx1 < kx1:
+ j += 1
+ if j >= q:
+ break
+ jid, _, jx0, jx1 = items0[j]
+ else:
+ k += 1
+ if k >= s:
+ break
+ kid, _, kx0, kx1 = items1[k]
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+if len(sys.argv) < 5:
+ print(
+ f'usage: {sys.argv[0]:s} layers.txt nets.txt net image_out'
+ )
+ sys.exit(1)
+layers_txt = sys.argv[1]
+nets_txt = sys.argv[2]
+net = int(sys.argv[3])
+image_out = sys.argv[4]
+
+with open(layers_txt) as fin:
+ line = fin.readline()
+ assert len(line)
+ n_layers, ys, xs = [int(i) for i in line.split()]
+
+ items = []
+ layers = []
+ for i in range(n_layers):
+ line = fin.readline()
+ assert len(line)
+ n_items = int(line.rstrip())
+ ids = {}
+ for j in range(n_items):
+ line = fin.readline()
+ assert len(line)
+ id, y, x0, x1 = [int(i) for i in line.split()]
+ if id not in ids:
+ ids[id] = []
+ ids[id].append((y, x0, x1))
+ layers.append(ids)
+
+image = numpy.zeros((ys, xs, 3), numpy.uint8)
+with open(nets_txt) as fin:
+ line = fin.readline()
+ while len(line):
+ net1, layer, id = [int(i) for i in line.split()]
+ if net1 == net:
+ c = layer + 1
+ c = numpy.array(
+ [((c >> 2) & 1) * 0xff, ((c >> 1) & 1) * 0xff, (c & 1) * 0xff],
+ numpy.uint8
+ )
+ for y, x0, x1 in layers[layer][id]:
+ image[y, x0:x1, :] = c[numpy.newaxis, :]
+ line = fin.readline()
+
+PIL.Image.fromarray(image).save(image_out)
--- /dev/null
+#!/usr/bin/env python3
+
+import numpy
+import sys
+
+line = sys.stdin.readline()
+assert len(line)
+n_layers, ys, xs = [int(i) for i in line.split()]
+
+items = []
+layers = []
+for i in range(n_layers):
+ line = sys.stdin.readline()
+ assert len(line)
+ n_items = int(line.rstrip())
+ ids = {}
+ for j in range(n_items):
+ line = sys.stdin.readline()
+ assert len(line)
+ id, _, _, _ = [int(i) for i in line.split()]
+ if id not in ids:
+ ids[id] = len(items)
+ items.append([len(items), i, id])
+ layers.append(ids)
+
+line = sys.stdin.readline()
+while len(line):
+ l0, l1, id0, id1 = [int(i) for i in line.split()]
+ # skip drain_source layer which separates nets by transistors
+ if l0 != 5 and l1 != 5:
+ net0 = layers[l0][id0]
+ n = items[net0][0]
+ while n != net0:
+ net0 = n
+ n = items[net0][0]
+
+ net1 = layers[l1][id1]
+ n = items[net1][0]
+ while n != net1:
+ net1 = n
+ n = items[net1][0]
+
+ net = min(net0, net1)
+
+ net0 = layers[l0][id0]
+ while net0 != net:
+ n = items[net0][0]
+ items[net0][0] = net
+ net0 = n
+
+ net1 = layers[l1][id1]
+ while net1 != net:
+ n = items[net1][0]
+ items[net1][0] = net
+ net1 = n
+ line = sys.stdin.readline()
+
+for i in range(len(items)):
+ net, layer, id = items[i]
+
+ n = items[net][0]
+ while n != net:
+ net = n
+ n = items[net][0]
+
+ n = i
+ while n != net:
+ o = items[n][0]
+ items[n][0] = net
+ n = o
+
+ print(net, layer, id)
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+PIL.Image.warnings.simplefilter('ignore', PIL.Image.DecompressionBombWarning)
+
+if len(sys.argv) < 3:
+ print(
+ f'usage: {sys.argv[0]:s} image_in image_out'
+ )
+ sys.exit(1)
+image_in = sys.argv[1]
+image_out = sys.argv[2]
+
+image = PIL.Image.open(image_in)
+image.load()
+#print('mode', image.mode)
+#print('palette', image.palette)
+#print('width', image.width)
+#print('height', image.height)
+image = numpy.array(image)
+#print('shape', image.shape)
+#print('dtype', image.dtype)
+assert len(image.shape) == 3 and image.dtype == numpy.uint8
+
+image = image != 0
+if image.shape[2] == 2 or image.shape[2] == 4:
+ image = image[:, :, :-1] & image[:, :, -1:]
+PIL.Image.fromarray(numpy.any(image, 2)).save(image_out)
--- /dev/null
+#!/usr/bin/env python3
+
+import PIL.Image
+import numpy
+import sys
+
+PIL.Image.warnings.simplefilter('ignore', PIL.Image.DecompressionBombWarning)
+
+if len(sys.argv) < 3:
+ print(
+ f'usage: {sys.argv[0]:s} image_in,image_in... image_out'
+ )
+ sys.exit(1)
+images_in = sys.argv[1].split(',')
+image_out = sys.argv[2]
+
+images = []
+for image_in in images_in:
+ image = PIL.Image.open(image_in)
+ image.load()
+ #print('mode', image.mode)
+ #print('palette', image.palette)
+ #print('width', image.width)
+ #print('height', image.height)
+ image = numpy.array(image)
+ #print('shape', image.shape)
+ #print('dtype', image.dtype)
+ assert len(image.shape) == 2 and image.dtype == bool
+ images.append(image)
+assert all([i.shape == images[0].shape for i in images])
+images = (images + [numpy.zeros_like(images[0])] * 3)[:3]
+image = numpy.stack(images, 2)
+
+PIL.Image.fromarray(image.astype(numpy.uint8) * 0xff).save(image_out)