mirror of https://github.com/efabless/caravel.git
Merge branch 'main' of https://github.com/efabless/caravel_openframe into main
This commit is contained in:
commit
de041996a6
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,616 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#
|
||||
# check_density.py ---
|
||||
#
|
||||
# Run density checks on the final (filled) GDS.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import subprocess
|
||||
|
||||
def usage():
|
||||
print("Usage:")
|
||||
print("check_density.py [<path_to_project>] [-keep]")
|
||||
print("")
|
||||
print("where:")
|
||||
print(" <path_to_project> is the path to the project top level directory.")
|
||||
print("")
|
||||
print(" If <path_to_project> is not given, then it is assumed to be the cwd.")
|
||||
print(" If '-keep' is specified, then keep the check script.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
optionlist = []
|
||||
arguments = []
|
||||
|
||||
debugmode = False
|
||||
keepmode = False
|
||||
|
||||
for option in sys.argv[1:]:
|
||||
if option.find('-', 0) == 0:
|
||||
optionlist.append(option)
|
||||
else:
|
||||
arguments.append(option)
|
||||
|
||||
if len(arguments) > 1:
|
||||
print("Wrong number of arguments given to check_density.py.")
|
||||
usage()
|
||||
sys.exit(0)
|
||||
|
||||
if len(arguments) == 1:
|
||||
user_project_path = arguments[0]
|
||||
else:
|
||||
user_project_path = os.getcwd()
|
||||
|
||||
# Check for valid user path
|
||||
|
||||
if not os.path.isdir(user_project_path):
|
||||
print('Error: Project path "' + user_project_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid user ID
|
||||
user_id_value = None
|
||||
if os.path.isfile(user_project_path + '/info.yaml'):
|
||||
with open(user_project_path + '/info.yaml', 'r') as ifile:
|
||||
infolines = ifile.read().splitlines()
|
||||
for line in infolines:
|
||||
kvpair = line.split(':')
|
||||
if len(kvpair) == 2:
|
||||
key = kvpair[0].strip()
|
||||
value = kvpair[1].strip()
|
||||
if key == 'project_id':
|
||||
user_id_value = value.strip('"\'')
|
||||
break
|
||||
|
||||
if user_id_value:
|
||||
project = 'caravel'
|
||||
project_with_id = 'caravel_' + user_id_value
|
||||
else:
|
||||
print('Error: No project_id found in info.yaml file.')
|
||||
sys.exit(1)
|
||||
|
||||
if '-debug' in optionlist:
|
||||
debugmode = True
|
||||
if '-keep' in optionlist:
|
||||
keepmode = True
|
||||
|
||||
magpath = user_project_path + '/mag'
|
||||
rcfile = magpath + '/.magicrc'
|
||||
|
||||
with open(magpath + '/check_density.tcl', 'w') as ofile:
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('crashbackups stop', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
|
||||
print('set starttime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Started reading GDS: $starttime"', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('flush stdout', file=ofile)
|
||||
print('update idletasks', file=ofile)
|
||||
|
||||
# Read final project from .gds
|
||||
print('gds readonly true', file=ofile)
|
||||
print('gds rescale false', file=ofile)
|
||||
print('gds read ../gds/' + project_with_id + '.gds', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
print('set midtime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Starting density checks: $midtime"', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('flush stdout', file=ofile)
|
||||
print('update idletasks', file=ofile)
|
||||
|
||||
# Get step box dimensions (700um for size and 70um for FOM step)
|
||||
# Use 350um for stepping on other layers.
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
# print('box size 700um 700um', file=ofile)
|
||||
# print('set stepbox [box values]', file=ofile)
|
||||
# print('set stepwidth [lindex $stepbox 2]', file=ofile)
|
||||
# print('set stepheight [lindex $stepbox 3]', file=ofile)
|
||||
|
||||
print('box size 70um 70um', file=ofile)
|
||||
print('set stepbox [box values]', file=ofile)
|
||||
print('set stepsizex [lindex $stepbox 2]', file=ofile)
|
||||
print('set stepsizey [lindex $stepbox 3]', file=ofile)
|
||||
|
||||
print('select top cell', file=ofile)
|
||||
print('expand', file=ofile)
|
||||
|
||||
# Modify the box to be inside the seal ring area (shrink 5um)
|
||||
print('box grow c -5um', file=ofile)
|
||||
print('set fullbox [box values]', file=ofile)
|
||||
|
||||
print('set xmax [lindex $fullbox 2]', file=ofile)
|
||||
print('set xmin [lindex $fullbox 0]', file=ofile)
|
||||
print('set fullwidth [expr {$xmax - $xmin}]', file=ofile)
|
||||
print('set xtiles [expr {int(ceil(($fullwidth + 0.0) / $stepsizex))}]', file=ofile)
|
||||
print('set ymax [lindex $fullbox 3]', file=ofile)
|
||||
print('set ymin [lindex $fullbox 1]', file=ofile)
|
||||
print('set fullheight [expr {$ymax - $ymin}]', file=ofile)
|
||||
print('set ytiles [expr {int(ceil(($fullheight + 0.0) / $stepsizey))}]', file=ofile)
|
||||
print('box size $stepsizex $stepsizey', file=ofile)
|
||||
print('set xbase [lindex $fullbox 0]', file=ofile)
|
||||
print('set ybase [lindex $fullbox 1]', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
print('puts stdout "XTILES: $xtiles"', file=ofile)
|
||||
print('puts stdout "YTILES: $ytiles"', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
# Need to know what fraction of a full tile is the last row and column
|
||||
print('set xfrac [expr {($xtiles * $stepsizex - $fullwidth + 0.0) / $stepsizex}]', file=ofile)
|
||||
print('set yfrac [expr {($ytiles * $stepsizey - $fullheight + 0.0) / $stepsizey}]', file=ofile)
|
||||
print('puts stdout "XFRAC: $xfrac"', file=ofile)
|
||||
print('puts stdout "YFRAC: $yfrac"', file=ofile)
|
||||
|
||||
print('cif ostyle density', file=ofile)
|
||||
|
||||
# Process density at steps. For efficiency, this is done in 70x70 um
|
||||
# areas, dumped to a file, and then aggregated into the 700x700 areas.
|
||||
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' set xlo [expr $xbase + $x * $stepsizex]', file=ofile)
|
||||
print(' set ylo [expr $ybase + $y * $stepsizey]', file=ofile)
|
||||
print(' set xhi [expr $xlo + $stepsizex]', file=ofile)
|
||||
print(' set yhi [expr $ylo + $stepsizey]', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
|
||||
# Flatten this area
|
||||
print(' flatten -dobbox -nolabels tile', file=ofile)
|
||||
print(' load tile', file=ofile)
|
||||
print(' select top cell', file=ofile)
|
||||
|
||||
# Run density check for each layer
|
||||
print(' puts stdout "Density results for tile x=$x y=$y"', file=ofile)
|
||||
|
||||
print(' set fdens [cif list cover fom_all]', file=ofile)
|
||||
print(' set pdens [cif list cover poly_all]', file=ofile)
|
||||
print(' set ldens [cif list cover li_all]', file=ofile)
|
||||
print(' set m1dens [cif list cover m1_all]', file=ofile)
|
||||
print(' set m2dens [cif list cover m2_all]', file=ofile)
|
||||
print(' set m3dens [cif list cover m3_all]', file=ofile)
|
||||
print(' set m4dens [cif list cover m4_all]', file=ofile)
|
||||
print(' set m5dens [cif list cover m5_all]', file=ofile)
|
||||
print(' puts stdout "FOM: $fdens"', file=ofile)
|
||||
print(' puts stdout "POLY: $pdens"', file=ofile)
|
||||
print(' puts stdout "LI1: $ldens"', file=ofile)
|
||||
print(' puts stdout "MET1: $m1dens"', file=ofile)
|
||||
print(' puts stdout "MET2: $m2dens"', file=ofile)
|
||||
print(' puts stdout "MET3: $m3dens"', file=ofile)
|
||||
print(' puts stdout "MET4: $m4dens"', file=ofile)
|
||||
print(' puts stdout "MET5: $m5dens"', file=ofile)
|
||||
print(' flush stdout', file=ofile)
|
||||
print(' update idletasks', file=ofile)
|
||||
|
||||
print(' load ' + project_with_id, file=ofile)
|
||||
print(' cellname delete tile', file=ofile)
|
||||
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
print('set endtime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Ended: $endtime"', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
|
||||
myenv = os.environ.copy()
|
||||
# Real views are necessary for the DRC checks
|
||||
myenv['MAGTYPE'] = 'mag'
|
||||
|
||||
print('Running density checks on file ' + project_with_id + '.gds', flush=True)
|
||||
|
||||
mproc = subprocess.Popen(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, magpath + '/check_density.tcl'],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
|
||||
# Use signal to poll the process and generate any output as it arrives
|
||||
|
||||
dlines = []
|
||||
|
||||
while mproc:
|
||||
status = mproc.poll()
|
||||
if status != None:
|
||||
try:
|
||||
output = mproc.communicate(timeout=1)
|
||||
except ValueError:
|
||||
print('Magic forced stop, status ' + str(status))
|
||||
sys.exit(1)
|
||||
else:
|
||||
outlines = output[0]
|
||||
errlines = output[1]
|
||||
for line in outlines.splitlines():
|
||||
dlines.append(line)
|
||||
print(line)
|
||||
for line in errlines.splitlines():
|
||||
print(line)
|
||||
print('Magic exited with status ' + str(status))
|
||||
if int(status) != 0:
|
||||
sys.exit(int(status))
|
||||
else:
|
||||
break
|
||||
else:
|
||||
n = 0
|
||||
while True:
|
||||
n += 1
|
||||
if n > 100:
|
||||
n = 0
|
||||
status = mproc.poll()
|
||||
if status != None:
|
||||
break
|
||||
sresult = select.select([mproc.stdout, mproc.stderr], [], [], 0.5)[0]
|
||||
if mproc.stdout in sresult:
|
||||
outstring = mproc.stdout.readline().strip()
|
||||
dlines.append(outstring)
|
||||
print(outstring)
|
||||
elif mproc.stderr in sresult:
|
||||
outstring = mproc.stderr.readline().strip()
|
||||
print(outstring)
|
||||
else:
|
||||
break
|
||||
|
||||
fomfill = []
|
||||
polyfill = []
|
||||
lifill = []
|
||||
met1fill = []
|
||||
met2fill = []
|
||||
met3fill = []
|
||||
met4fill = []
|
||||
met5fill = []
|
||||
xtiles = 0
|
||||
ytiles = 0
|
||||
xfrac = 0.0
|
||||
yfrac = 0.0
|
||||
|
||||
for line in dlines:
|
||||
dpair = line.split(':')
|
||||
if len(dpair) == 2:
|
||||
layer = dpair[0]
|
||||
try:
|
||||
density = float(dpair[1].strip())
|
||||
except:
|
||||
continue
|
||||
if layer == 'FOM':
|
||||
fomfill.append(density)
|
||||
elif layer == 'POLY':
|
||||
polyfill.append(density)
|
||||
elif layer == 'LI1':
|
||||
lifill.append(density)
|
||||
elif layer == 'MET1':
|
||||
met1fill.append(density)
|
||||
elif layer == 'MET2':
|
||||
met2fill.append(density)
|
||||
elif layer == 'MET3':
|
||||
met3fill.append(density)
|
||||
elif layer == 'MET4':
|
||||
met4fill.append(density)
|
||||
elif layer == 'MET5':
|
||||
met5fill.append(density)
|
||||
elif layer == 'XTILES':
|
||||
xtiles = int(dpair[1].strip())
|
||||
elif layer == 'YTILES':
|
||||
ytiles = int(dpair[1].strip())
|
||||
elif layer == 'XFRAC':
|
||||
xfrac = float(dpair[1].strip())
|
||||
elif layer == 'YFRAC':
|
||||
yfrac = float(dpair[1].strip())
|
||||
|
||||
if ytiles == 0 or xtiles == 0:
|
||||
print('Failed to read XTILES or YTILES from output.')
|
||||
sys.exit(1)
|
||||
|
||||
total_tiles = (ytiles - 9) * (xtiles - 9)
|
||||
|
||||
print('')
|
||||
print('Density results (total tiles = ' + str(total_tiles) + '):')
|
||||
|
||||
# For FOM, step at 70um intervals (same as 70um check area)
|
||||
fomstep = 1
|
||||
|
||||
# For poly, step only at 700um intervals (10 * 70um check area)
|
||||
polystep = 10
|
||||
|
||||
# For all metals, step only at 350um intervals (5 * 70um check area)
|
||||
metalstep = 5
|
||||
|
||||
# Full areas are 10 x 10 tiles = 100. But the right and top sides are
|
||||
# not full tiles, so the full area must be prorated.
|
||||
|
||||
sideadjust = 90.0 + (10.0 * xfrac)
|
||||
topadjust = 90.0 + (10.0 * yfrac)
|
||||
corneradjust = 81.0 + (9.0 * xfrac) + (9.0 * yfrac) + (xfrac * yfrac)
|
||||
|
||||
print('')
|
||||
print('FOM Density:')
|
||||
for y in range(0, ytiles - 9, fomstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, fomstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
fomaccum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
fomaccum += sum(fomfill[base : base + 10])
|
||||
|
||||
fomaccum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(fomaccum))
|
||||
if fomaccum < 0.33:
|
||||
print('***Error: FOM Density < 33%')
|
||||
elif fomaccum > 0.57:
|
||||
print('***Error: FOM Density > 57%')
|
||||
|
||||
print('')
|
||||
print('POLY Density:')
|
||||
for y in range(0, ytiles - 9, polystep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, polystep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
polyaccum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
polyaccum += sum(polyfill[base : base + 10])
|
||||
|
||||
polyaccum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(polyaccum))
|
||||
|
||||
print('')
|
||||
print('LI Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
liaccum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
liaccum += sum(lifill[base : base + 10])
|
||||
|
||||
liaccum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(liaccum))
|
||||
if liaccum < 0.35:
|
||||
print('***Error: LI Density < 35%')
|
||||
elif liaccum > 0.60:
|
||||
print('***Error: LI Density > 60%')
|
||||
|
||||
print('')
|
||||
print('MET1 Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
met1accum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
met1accum += sum(met1fill[base : base + 10])
|
||||
|
||||
met1accum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(met1accum))
|
||||
if met1accum < 0.35:
|
||||
print('***Error: MET1 Density < 35%')
|
||||
elif met1accum > 0.60:
|
||||
print('***Error: MET1 Density > 60%')
|
||||
|
||||
print('')
|
||||
print('MET2 Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
met2accum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
met2accum += sum(met2fill[base : base + 10])
|
||||
|
||||
met2accum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(met2accum))
|
||||
if met2accum < 0.35:
|
||||
print('***Error: MET2 Density < 35%')
|
||||
elif met2accum > 0.60:
|
||||
print('***Error: MET2 Density > 60%')
|
||||
|
||||
print('')
|
||||
print('MET3 Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
met3accum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
met3accum += sum(met3fill[base : base + 10])
|
||||
|
||||
met3accum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(met3accum))
|
||||
if met3accum < 0.35:
|
||||
print('***Error: MET3 Density < 35%')
|
||||
elif met3accum > 0.60:
|
||||
print('***Error: MET3 Density > 60%')
|
||||
|
||||
print('')
|
||||
print('MET4 Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
met4accum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
met4accum += sum(met4fill[base : base + 10])
|
||||
|
||||
met4accum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(met4accum))
|
||||
if met4accum < 0.35:
|
||||
print('***Error: MET4 Density < 35%')
|
||||
elif met4accum > 0.60:
|
||||
print('***Error: MET4 Density > 60%')
|
||||
|
||||
print('')
|
||||
print('MET5 Density:')
|
||||
for y in range(0, ytiles - 9, metalstep):
|
||||
if y == ytiles - 10:
|
||||
atotal = topadjust
|
||||
else:
|
||||
atotal = 100.0
|
||||
for x in range(0, xtiles - 9, metalstep):
|
||||
if x == xtiles - 10:
|
||||
if y == ytiles - 10:
|
||||
atotal = corneradjust
|
||||
else:
|
||||
atotal = sideadjust
|
||||
met5accum = 0
|
||||
for w in range(y, y + 10):
|
||||
base = xtiles * w + x
|
||||
met5accum += sum(met5fill[base : base + 10])
|
||||
|
||||
met5accum /= atotal
|
||||
print('Tile (' + str(x) + ', ' + str(y) + '): ' + str(met5accum))
|
||||
if met5accum < 0.45:
|
||||
print('***Error: MET5 Density < 45%')
|
||||
elif met5accum > 0.76:
|
||||
print('***Error: MET5 Density > 76%')
|
||||
|
||||
print('')
|
||||
print('Whole-chip density results:')
|
||||
|
||||
atotal = ((xtiles - 1.0) * (ytiles - 1.0)) + ((ytiles - 1.0) * xfrac) + ((xtiles - 1.0) * yfrac) + (xfrac * yfrac)
|
||||
|
||||
fomaccum = sum(fomfill) / atotal
|
||||
print('')
|
||||
print('FOM Density: ' + str(fomaccum))
|
||||
if fomaccum < 0.33:
|
||||
print('***Error: FOM Density < 33%')
|
||||
elif fomaccum > 0.57:
|
||||
print('***Error: FOM Density > 57%')
|
||||
|
||||
polyaccum = sum(polyfill) / atotal
|
||||
print('')
|
||||
print('POLY Density: ' + str(polyaccum))
|
||||
|
||||
liaccum = sum(lifill) / atotal
|
||||
print('')
|
||||
print('LI Density: ' + str(liaccum))
|
||||
if liaccum < 0.35:
|
||||
print('***Error: LI Density < 35%')
|
||||
elif liaccum > 0.60:
|
||||
print('***Error: LI Density > 60%')
|
||||
|
||||
met1accum = sum(met1fill) / atotal
|
||||
print('')
|
||||
print('MET1 Density: ' + str(met1accum))
|
||||
if met1accum < 0.35:
|
||||
print('***Error: MET1 Density < 35%')
|
||||
elif met1accum > 0.60:
|
||||
print('***Error: MET1 Density > 60%')
|
||||
|
||||
met2accum = sum(met2fill) / atotal
|
||||
print('')
|
||||
print('MET2 Density: ' + str(met2accum))
|
||||
if met2accum < 0.35:
|
||||
print('***Error: MET2 Density < 35%')
|
||||
elif met2accum > 0.60:
|
||||
print('***Error: MET2 Density > 60%')
|
||||
|
||||
met3accum = sum(met3fill) / atotal
|
||||
print('')
|
||||
print('MET3 Density: ' + str(met3accum))
|
||||
if met3accum < 0.35:
|
||||
print('***Error: MET3 Density < 35%')
|
||||
elif met3accum > 0.60:
|
||||
print('***Error: MET3 Density > 60%')
|
||||
|
||||
met4accum = sum(met4fill) / atotal
|
||||
print('')
|
||||
print('MET4 Density: ' + str(met4accum))
|
||||
if met4accum < 0.35:
|
||||
print('***Error: MET4 Density < 35%')
|
||||
elif met4accum > 0.60:
|
||||
print('***Error: MET4 Density > 60%')
|
||||
|
||||
met5accum = sum(met5fill) / atotal
|
||||
print('')
|
||||
print('MET5 Density: ' + str(met5accum))
|
||||
if met5accum < 0.45:
|
||||
print('***Error: MET5 Density < 45%')
|
||||
elif met5accum > 0.76:
|
||||
print('***Error: MET5 Density > 76%')
|
||||
|
||||
if not keepmode:
|
||||
if os.path.isfile(magpath + '/check_density.tcl'):
|
||||
os.remove(magpath + '/check_density.tcl')
|
||||
|
||||
print('')
|
||||
print('Done!')
|
||||
sys.exit(0)
|
|
@ -0,0 +1,253 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#
|
||||
# compositor.py ---
|
||||
#
|
||||
# Compose the final GDS for caravel from the caravel GDS, seal ring
|
||||
# GDS, and fill GDS.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
def usage():
|
||||
print("Usage:")
|
||||
print("compositor.py <user_id_value> <project> <path_to_project> <path_to_mag_dir> <path_to_gds_dir [-keep]")
|
||||
print("")
|
||||
print("where:")
|
||||
print(" <user_id_value> is a character string of eight hex digits, and")
|
||||
print(" <path_to_project> is the path to the project top level directory.")
|
||||
print(" <path_to_mag_dir> is the path to the mag directory.")
|
||||
print(" <path_to_gds_dir> is the path to the gds directory.")
|
||||
print("")
|
||||
print(" If <user_id_value> is not given, then it must exist in the info.yaml file.")
|
||||
print(" If <path_to_project> is not given, then it is assumed to be the cwd.")
|
||||
print(" If <path_to_mag_dir> is not given, then it is assumed to be the <path_to_project>/tmp.")
|
||||
print(" If <path_to_gds_dir> is not given, then it is assumed to be the <path_to_project>/gds.")
|
||||
print(" If '-keep' is specified, then keep the generation script.")
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
optionlist = []
|
||||
arguments = []
|
||||
|
||||
debugmode = False
|
||||
keepmode = False
|
||||
|
||||
for option in sys.argv[1:]:
|
||||
if option.find('-', 0) == 0:
|
||||
optionlist.append(option)
|
||||
else:
|
||||
arguments.append(option)
|
||||
|
||||
if len(arguments) != 5:
|
||||
print("Wrong number of arguments given to compositor.py.")
|
||||
usage()
|
||||
sys.exit(0)
|
||||
|
||||
user_id_value = arguments[0]
|
||||
project = arguments[1]
|
||||
user_project_path = arguments[2]
|
||||
mag_dir_path = arguments[3]
|
||||
gds_dir_path = arguments[4]
|
||||
|
||||
# if len(arguments) > 0:
|
||||
# user_id_value = arguments[0]
|
||||
|
||||
# Convert to binary
|
||||
try:
|
||||
user_id_int = int('0x' + user_id_value, 0)
|
||||
user_id_bits = '{0:032b}'.format(user_id_int)
|
||||
except:
|
||||
print("User ID not recognized")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
# if len(arguments) == 2 and user_project_path == None:
|
||||
# user_project_path = arguments[1]
|
||||
# mag_dir_path = user_project_path + "/mag"
|
||||
# gds_dir_path = "../gds"
|
||||
# if len(arguments) == 3 and user_project_path == None:
|
||||
# user_project_path = arguments[1]
|
||||
# mag_dir_path = arguments[2]
|
||||
# gds_dir_path = "../gds"
|
||||
# if len(arguments) == 4:
|
||||
# user_project_path = arguments[1]
|
||||
# mag_dir_path = arguments[2]
|
||||
# gds_dir_path = arguments[3]
|
||||
# elif len(arguments) == 3 and user_project_path != None:
|
||||
# mag_dir_path = arguments[1]
|
||||
# gds_dir_path = arguments[2]
|
||||
# else:
|
||||
# user_project_path = os.getcwd()
|
||||
# mag_dir_path = user_project_path + "/mag"
|
||||
# gds_dir_path = "../gds"
|
||||
|
||||
# Check for valid user path
|
||||
|
||||
if not os.path.isdir(user_project_path):
|
||||
print('Error: Project path "' + user_project_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid mag path
|
||||
|
||||
if not os.path.isdir(mag_dir_path):
|
||||
print('Error: Mag directory path "' + mag_dir_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid gds path
|
||||
|
||||
if not os.path.isdir(gds_dir_path):
|
||||
print('Error: GDS directory path "' + gds_dir_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid user ID
|
||||
# if not user_id_value:
|
||||
# if os.path.isfile(user_project_path + '/info.yaml'):
|
||||
# with open(user_project_path + '/info.yaml', 'r') as ifile:
|
||||
# infolines = ifile.read().splitlines()
|
||||
# for line in infolines:
|
||||
# kvpair = line.split(':')
|
||||
# if len(kvpair) == 2:
|
||||
# key = kvpair[0].strip()
|
||||
# value = kvpair[1].strip()
|
||||
# if key == 'project_id':
|
||||
# user_id_value = value.strip('"\'')
|
||||
# break
|
||||
|
||||
if user_id_value:
|
||||
# project = 'caravel'
|
||||
# project_with_id = project + '_' + user_id_value
|
||||
project_with_id = 'caravel_' + user_id_value
|
||||
user_id_decimal = str(int(user_id_value, 16))
|
||||
else:
|
||||
print('Error: No project_id found in info.yaml file.')
|
||||
sys.exit(1)
|
||||
|
||||
if '-debug' in optionlist:
|
||||
debugmode = True
|
||||
if '-keep' in optionlist:
|
||||
keepmode = True
|
||||
|
||||
magpath = mag_dir_path
|
||||
rcfile = magpath + '/.magicrc'
|
||||
|
||||
gdspath = gds_dir_path
|
||||
|
||||
# The compositor script will create <project_with_id>.mag, but is uses
|
||||
# "load", so the file must not already exist.
|
||||
|
||||
if os.path.isfile(user_project_path + '/mag/' + project_with_id + '.mag'):
|
||||
print('Error: File ' + project_with_id + '.mag exists already! Exiting. . .')
|
||||
sys.exit(1)
|
||||
|
||||
with open(user_project_path + '/mag/compose_final.tcl', 'w') as ofile:
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
# Set the random seed from the project ID
|
||||
print('random seed ' + user_id_decimal, file=ofile)
|
||||
|
||||
# Read project from .mag but set GDS properties so that it points
|
||||
# to the GDS file created by "make ship".
|
||||
print('load ' + project + ' -dereference', file=ofile)
|
||||
print('property GDS_FILE ' + gdspath + '/' + project + '.gds', file=ofile)
|
||||
print('property GDS_START 0', file=ofile)
|
||||
print('select top cell', file=ofile)
|
||||
print('set bbox [box values]', file=ofile)
|
||||
|
||||
# Ceate a cell to represent the generated fill. There are
|
||||
# no magic layers corresponding to the fill shape data, and
|
||||
# it's gigabytes anyway, so we don't want to deal with any
|
||||
# actual data. So it's just a placeholder.
|
||||
|
||||
print('load ' + project_with_id + '_fill_pattern -quiet', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
print('box values {*}$bbox', file=ofile)
|
||||
print('paint comment', file=ofile)
|
||||
print('property GDS_FILE ' + gdspath + '/' + project_with_id + '_fill_pattern.gds', file=ofile)
|
||||
print('property GDS_START 0', file=ofile)
|
||||
print('property FIXED_BBOX "$bbox"', file=ofile)
|
||||
|
||||
# Create a new project top level and place the fill cell.
|
||||
print('load ' + project_with_id + ' -quiet', file=ofile)
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
print('box position 6um 6um', file=ofile)
|
||||
print('getcell ' + project + ' child 0 0', file=ofile)
|
||||
print('getcell ' + project_with_id + '_fill_pattern child 0 0', file=ofile)
|
||||
|
||||
# Move existing origin to (6um, 6um) for seal ring placement
|
||||
# print('move origin -6um -6um', file=ofile)
|
||||
|
||||
# Read in abstract view of seal ring
|
||||
print('box position 0 0', file=ofile)
|
||||
print('getcell advSeal_6um_gen', file=ofile)
|
||||
|
||||
# Write out completed project as "caravel_" + the user ID
|
||||
# print('save ' + user_project_path + '/mag/' + project_with_id, file=ofile)
|
||||
|
||||
# Generate final GDS
|
||||
print('puts stdout "Writing final GDS. . . "', file=ofile)
|
||||
print('flush stdout', file=ofile)
|
||||
print('gds undefined allow', file=ofile)
|
||||
print('cif *hier write disable', file=ofile)
|
||||
print('gds write ' + gdspath + '/' + project_with_id + '.gds', file=ofile)
|
||||
print('quit -noprompt', file=ofile)
|
||||
|
||||
myenv = os.environ.copy()
|
||||
# Abstract views are appropriate for final composition
|
||||
myenv['MAGTYPE'] = 'maglef'
|
||||
|
||||
print('Building final GDS file ' + project_with_id + '.gds', flush=True)
|
||||
|
||||
mproc = subprocess.run(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, user_project_path + '/mag/compose_final.tcl'],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
if mproc.stdout:
|
||||
for line in mproc.stdout.splitlines():
|
||||
print(line)
|
||||
if mproc.stderr:
|
||||
# NOTE: Until there is a "load -quiet" option in magic, loading
|
||||
# a new cell generates an error. This code ignores the error.
|
||||
newlines = []
|
||||
for line in mproc.stderr.splitlines():
|
||||
if line.endswith("_fill_pattern.mag couldn't be read"):
|
||||
continue
|
||||
if line.startswith("No such file or directory"):
|
||||
continue
|
||||
else:
|
||||
newlines.append(line)
|
||||
|
||||
if len(newlines) > 0:
|
||||
print('Error message output from magic:')
|
||||
for line in newlines:
|
||||
print(line)
|
||||
if mproc.returncode != 0:
|
||||
print('ERROR: Magic exited with status ' + str(mproc.returncode))
|
||||
|
||||
if not keepmode:
|
||||
os.remove(user_project_path + '/mag/compose_final.tcl')
|
||||
|
||||
print('Done!')
|
||||
exit(0)
|
|
@ -0,0 +1,121 @@
|
|||
#!ENV_PATH python3
|
||||
#
|
||||
#---------------------------------------------------------
|
||||
# LVS failure check
|
||||
#
|
||||
# This is a Python script that parses the comp.json
|
||||
# output from netgen and reports on the number of
|
||||
# errors in the top-level netlist.
|
||||
#
|
||||
#---------------------------------------------------------
|
||||
# Written by Tim Edwards
|
||||
# efabless, inc.
|
||||
# Pulled from qflow GUI as standalone script Aug 20, 2018
|
||||
#---------------------------------------------------------
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
|
||||
def count_LVS_failures(filename):
|
||||
with open(filename, 'r') as cfile:
|
||||
lvsdata = json.load(cfile)
|
||||
|
||||
# Count errors in the JSON file
|
||||
failures = 0
|
||||
devfail = 0
|
||||
netfail = 0
|
||||
pinfail = 0
|
||||
propfail = 0
|
||||
netdiff = 0
|
||||
devdiff = 0
|
||||
ncells = len(lvsdata)
|
||||
for c in range(0, ncells):
|
||||
cellrec = lvsdata[c]
|
||||
|
||||
if c == ncells - 1:
|
||||
topcell = True
|
||||
else:
|
||||
topcell = False
|
||||
|
||||
# Most errors must only be counted for the top cell, because individual
|
||||
# failing cells are flattened and the matching attempted again on the
|
||||
# flattened netlist.
|
||||
|
||||
if topcell:
|
||||
if 'devices' in cellrec:
|
||||
devices = cellrec['devices']
|
||||
devlist = [val for pair in zip(devices[0], devices[1]) for val in pair]
|
||||
devpair = list(devlist[p:p + 2] for p in range(0, len(devlist), 2))
|
||||
for dev in devpair:
|
||||
c1dev = dev[0]
|
||||
c2dev = dev[1]
|
||||
diffdevs = abs(c1dev[1] - c2dev[1])
|
||||
failures += diffdevs
|
||||
devdiff += diffdevs
|
||||
|
||||
if 'nets' in cellrec:
|
||||
nets = cellrec['nets']
|
||||
diffnets = abs(nets[0] - nets[1])
|
||||
failures += diffnets
|
||||
netdiff += diffnets
|
||||
|
||||
if 'badnets' in cellrec:
|
||||
badnets = cellrec['badnets']
|
||||
failures += len(badnets)
|
||||
netfail += len(badnets)
|
||||
|
||||
if 'badelements' in cellrec:
|
||||
badelements = cellrec['badelements']
|
||||
failures += len(badelements)
|
||||
devfail += len(badelements)
|
||||
|
||||
if 'pins' in cellrec:
|
||||
pins = cellrec['pins']
|
||||
pinlist = [val for pair in zip(pins[0], pins[1]) for val in pair]
|
||||
pinpair = list(pinlist[p:p + 2] for p in range(0, len(pinlist), 2))
|
||||
for pin in pinpair:
|
||||
# Avoid flagging global vs. local names, e.g., "gnd" vs. "gnd!,"
|
||||
# and ignore case when comparing pins.
|
||||
pin0 = re.sub('!$', '', pin[0].lower())
|
||||
pin1 = re.sub('!$', '', pin[1].lower())
|
||||
if pin0 != pin1:
|
||||
# The text "(no pin)" indicates a missing pin that can be
|
||||
# ignored because the pin in the other netlist is a no-connect
|
||||
if pin0 != '(no pin)' and pin1 != '(no pin)':
|
||||
failures += 1
|
||||
pinfail += 1
|
||||
|
||||
# Property errors must be counted for every cell
|
||||
if 'properties' in cellrec:
|
||||
properties = cellrec['properties']
|
||||
failures += len(properties)
|
||||
propfail += len(properties)
|
||||
|
||||
return [failures, netfail, devfail, pinfail, propfail, netdiff, devdiff]
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser(description='Parses netgen lvs')
|
||||
parser.add_argument('--file', '-f', required=True)
|
||||
args = parser.parse_args()
|
||||
failures = count_LVS_failures(args.file)
|
||||
|
||||
total = failures[0]
|
||||
if total > 0:
|
||||
failed = True
|
||||
print('LVS reports:')
|
||||
print(' net count difference = ' + str(failures[5]))
|
||||
print(' device count difference = ' + str(failures[6]))
|
||||
print(' unmatched nets = ' + str(failures[1]))
|
||||
print(' unmatched devices = ' + str(failures[2]))
|
||||
print(' unmatched pins = ' + str(failures[3]))
|
||||
print(' property failures = ' + str(failures[4]))
|
||||
else:
|
||||
print('LVS reports no net, device, pin, or property mismatches.')
|
||||
|
||||
print('')
|
||||
print('Total errors = ' + str(total))
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
from tempfile import mkstemp
|
||||
import re
|
||||
|
||||
|
||||
def remove_inouts(jsonpath, replacewith='input'):
|
||||
"""Replaces inouts with either input or output statements.
|
||||
|
||||
Netlistsvg does not parse inout ports as for now, so they need to be
|
||||
replaced with either input or output to produce a diagram.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
jsonpath : str
|
||||
Path to JSON file to fix
|
||||
replacewith : str
|
||||
The string to replace 'inout', can be 'input' or 'output'
|
||||
"""
|
||||
assert replacewith in ['input', 'output']
|
||||
with open(jsonpath, 'r') as withinouts:
|
||||
lines = withinouts.readlines()
|
||||
with open(jsonpath, 'w') as withoutinouts:
|
||||
for line in lines:
|
||||
withoutinouts.write(re.sub('inout', replacewith, line))
|
||||
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser(argv[0])
|
||||
parser.add_argument(
|
||||
'verilog_rtl_dir',
|
||||
help="Path to the project's verilog/rtl directory",
|
||||
type=Path)
|
||||
parser.add_argument(
|
||||
'output',
|
||||
help="Path to the output SVG file",
|
||||
type=Path)
|
||||
parser.add_argument(
|
||||
'--num-iopads',
|
||||
help='Number of iopads to render',
|
||||
type=int,
|
||||
default=38)
|
||||
parser.add_argument(
|
||||
'--yosys-executable',
|
||||
help='Path to yosys executable',
|
||||
type=Path,
|
||||
default='yosys')
|
||||
parser.add_argument(
|
||||
'--netlistsvg-executable',
|
||||
help='Path to netlistsvg executable',
|
||||
type=Path,
|
||||
default='netlistsvg')
|
||||
parser.add_argument(
|
||||
'--inouts-as',
|
||||
help='To what kind of IO should inout ports be replaced',
|
||||
choices=['input', 'output'],
|
||||
default='input'
|
||||
)
|
||||
|
||||
args = parser.parse_args(argv[1:])
|
||||
|
||||
fd, jsonpath = mkstemp(suffix='-yosys.json')
|
||||
os.close(fd)
|
||||
|
||||
yosyscommand = [
|
||||
f'{str(args.yosys_executable)}',
|
||||
'-p',
|
||||
'read_verilog pads.v defines.v; ' +
|
||||
'read_verilog -lib -overwrite *.v; ' +
|
||||
f'verilog_defines -DMPRJ_IO_PADS={args.num_iopads}; ' +
|
||||
'read_verilog -overwrite caravel.v; ' +
|
||||
'hierarchy -top caravel; ' +
|
||||
'proc; ' +
|
||||
'opt; ' +
|
||||
f'write_json {jsonpath}; '
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
yosyscommand,
|
||||
cwd=args.verilog_rtl_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
|
||||
exitcode = 0
|
||||
if result.returncode != 0:
|
||||
print(f'Failed to run: {" ".join(yosyscommand)}', file=sys.stderr)
|
||||
print(result.stdout.decode())
|
||||
exitcode = result.returncode
|
||||
else:
|
||||
# TODO once netlistsvg supports inout ports, this should be removed
|
||||
remove_inouts(jsonpath, args.inouts_as)
|
||||
command = f'{args.netlistsvg_executable} {jsonpath} -o {args.output}'
|
||||
result = subprocess.run(
|
||||
command.split(),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f'Failed to run: {command}', file=sys.stderr)
|
||||
print(result.stdout.decode())
|
||||
exitcode = result.returncode
|
||||
|
||||
os.unlink(jsonpath)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
|
@ -1,318 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
#
|
||||
# gen_gpio_defaults.py ---
|
||||
#
|
||||
# Manipulate the magic database and GDS to create and apply defaults
|
||||
# to the GPIO control blocks based on the user's specification in the
|
||||
# user_defines.v file.
|
||||
#
|
||||
# The GPIO defaults block contains 13 bits that set the state of the
|
||||
# GPIO on power-up. GPIOs 0 to 4 in the user project area are fixed
|
||||
# and cannot be modified (to maintain access to the housekeeping SPI
|
||||
# on startup). GPIOs 5 to 37 are by default set to be an input pad
|
||||
# controlled by the user project. The file "user_defines.v" contains
|
||||
# the state specified by the user for each GPIO pad, and is what is
|
||||
# used in verilog simulation.
|
||||
#
|
||||
# This script parses the user_defines.v file to determine the state
|
||||
# of each GPIO. Then it creates as many new layouts as needed to
|
||||
# represent all unique states, modifies the caravel.mag layout
|
||||
# to replace the default layouts with the new ones as needed, and
|
||||
# generates GDS files for each of the layouts.
|
||||
#
|
||||
# gpio_defaults_block layout map:
|
||||
# Positions marked (in microns) for value = 0. For value = 1, move
|
||||
# the via 0.69um to the left. The given position is the lower left
|
||||
# corner position of the via. The via itself is 0.17um x 0.17um.
|
||||
# The values below are for the file gpio_defaults_block_1403.
|
||||
# Positions marked "Y" for "Programmed One?" are already moved to
|
||||
# the left, and so should be move 0.69um to the right if the bit
|
||||
# should be zero.
|
||||
#
|
||||
# Signal Via position (um)
|
||||
# name X Y
|
||||
#-------------------------------------------------------------------
|
||||
# gpio_defaults[0] 5.435 4.165
|
||||
# gpio_defaults[1] 6.815 3.825
|
||||
# gpio_defaults[2] 8.195 4.165
|
||||
# gpio_defaults[3] 9.575 3.825
|
||||
# gpio_defaults[4] 10.955 3.825
|
||||
# gpio_defaults[5] 12.565 3.825
|
||||
# gpio_defaults[6] 14.865 3.825
|
||||
# gpio_defaults[7] 17.165 3.825
|
||||
# gpio_defaults[8] 19.465 3.825
|
||||
# gpio_defaults[9] 21.765 3.825
|
||||
# gpio_defaults[10] 24.755 3.825
|
||||
# gpio_defaults[11] 27.055 3.825
|
||||
# gpio_defaults[12] 23.605 4.165
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
def usage():
|
||||
print('Usage:')
|
||||
print('gen_gpio_defaults.py [<path_to_project>]')
|
||||
print('')
|
||||
print('where:')
|
||||
print(' <path_to_project> is the path to the project top level directory.')
|
||||
print('')
|
||||
print(' If <path_to_project> is not given, then it is assumed to be the cwd.')
|
||||
print(' The file "user_defines.v" must exist in verilog/rtl/ relative to')
|
||||
print(' <path_to_project>.')
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# Coordinate pairs in microns for the zero position on each bit
|
||||
via_pos = [[5.435, 4.165], [6.815, 3.825], [8.195, 4.165], [9.575, 3.825],
|
||||
[10.955, 3.825], [12.565, 3.825], [14.865, 3.825], [17.165, 3.825],
|
||||
[19.465, 3.825], [21.765, 3.825], [24.755, 3.825], [27.055, 3.825],
|
||||
[23.605, 4.165]]
|
||||
|
||||
optionlist = []
|
||||
arguments = []
|
||||
|
||||
debugmode = False
|
||||
testmode = False
|
||||
|
||||
for option in sys.argv[1:]:
|
||||
if option.find('-', 0) == 0:
|
||||
optionlist.append(option)
|
||||
else:
|
||||
arguments.append(option)
|
||||
|
||||
if len(arguments) > 2:
|
||||
print("Wrong number of arguments given to gen_gpio_defaults.py.")
|
||||
usage()
|
||||
sys.exit(0)
|
||||
|
||||
if '-debug' in optionlist:
|
||||
debugmode = True
|
||||
if '-test' in optionlist:
|
||||
testmode = True
|
||||
|
||||
user_project_path = None
|
||||
|
||||
if len(arguments) == 0:
|
||||
user_project_path = os.getcwd()
|
||||
else:
|
||||
user_project_path = arguments[0]
|
||||
|
||||
if not os.path.isdir(user_project_path):
|
||||
print('Error: Project path "' + user_project_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
magpath = user_project_path + '/mag'
|
||||
gdspath = user_project_path + '/gds'
|
||||
vpath = user_project_path + '/verilog'
|
||||
|
||||
# Check paths
|
||||
if not os.path.isdir(gdspath):
|
||||
print('No directory ' + gdspath + ' found (path to GDS).')
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(vpath):
|
||||
print('No directory ' + vpath + ' found (path to verilog).')
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.isdir(magpath):
|
||||
print('No directory ' + magpath + ' found (path to magic databases).')
|
||||
sys.exit(1)
|
||||
|
||||
# Parse the user defines verilog file
|
||||
kvpairs = {}
|
||||
if os.path.isfile(vpath + '/rtl/user_defines.v'):
|
||||
with open(vpath + '/rtl/user_defines.v', 'r') as ifile:
|
||||
infolines = ifile.read().splitlines()
|
||||
for line in infolines:
|
||||
tokens = line.split()
|
||||
if len(tokens) >= 3:
|
||||
if tokens[0] == '`define':
|
||||
if tokens[2][0] == '`':
|
||||
# If definition is nested, substitute value.
|
||||
tokens[2] = kvpairs[tokens[2]]
|
||||
kvpairs['`' + tokens[1]] = tokens[2]
|
||||
else:
|
||||
print('Error: No user_defines.v file found.')
|
||||
sys.exit(1)
|
||||
|
||||
# Set additional dictionary entries for the fixed-configuration
|
||||
# GPIOs 0 to 4. This allows the layout to have the default
|
||||
# gpio_defaults_block layout, and this script will change it as
|
||||
# needed.
|
||||
|
||||
kvpairs["`USER_CONFIG_GPIO_0_INIT"] = "13'h1803"
|
||||
kvpairs["`USER_CONFIG_GPIO_1_INIT"] = "13'h1803"
|
||||
kvpairs["`USER_CONFIG_GPIO_2_INIT"] = "13'h0403"
|
||||
kvpairs["`USER_CONFIG_GPIO_3_INIT"] = "13'h0403"
|
||||
kvpairs["`USER_CONFIG_GPIO_4_INIT"] = "13'h0403"
|
||||
|
||||
# Generate zero and one coordinates for each via
|
||||
llx_zero = []
|
||||
lly_zero = []
|
||||
urx_zero = []
|
||||
ury_zero = []
|
||||
llx_one = []
|
||||
lly_one = []
|
||||
urx_one = []
|
||||
ury_one = []
|
||||
|
||||
zero_string = []
|
||||
one_string = []
|
||||
|
||||
for i in range(0, 13):
|
||||
llx_zero = int(via_pos[i][0] * 200)
|
||||
lly_zero = int(via_pos[i][1] * 200)
|
||||
urx_zero = llx_zero + 34
|
||||
ury_zero = lly_zero + 34
|
||||
|
||||
llx_one = llx_zero - 138
|
||||
lly_one = lly_zero
|
||||
urx_one = urx_zero - 138
|
||||
ury_one = ury_zero
|
||||
|
||||
zero_string.append('rect {:d} {:d} {:d} {:d}'.format(llx_zero, lly_zero, urx_zero, ury_zero))
|
||||
one_string.append('rect {:d} {:d} {:d} {:d}'.format(llx_one, lly_one, urx_one, ury_one))
|
||||
|
||||
# Create new cells for each unique type
|
||||
print('Step 1: Create new cells for new GPIO default vectors.')
|
||||
|
||||
cellsused = [None] * 38
|
||||
|
||||
for i in range(5, 38):
|
||||
config_name = '`USER_CONFIG_GPIO_' + str(i) + '_INIT'
|
||||
try:
|
||||
config_value = kvpairs[config_name]
|
||||
except:
|
||||
print('No configuration specified for GPIO ' + str(i) + '; skipping.')
|
||||
continue
|
||||
|
||||
try:
|
||||
default_str = config_value[-4:]
|
||||
binval = '{:013b}'.format(int(default_str, 16))
|
||||
except:
|
||||
print('Error: Default value ' + config_value + ' is not a 4-digit hex number; skipping')
|
||||
continue
|
||||
|
||||
cell_name = 'gpio_defaults_block_' + default_str
|
||||
mag_file = magpath + '/' + cell_name + '.mag'
|
||||
cellsused[i] = cell_name
|
||||
|
||||
if not os.path.isfile(mag_file):
|
||||
# A cell with this set of defaults doesn't exist, so make it
|
||||
# First read the 0000 cell, then write to mag_path while
|
||||
# changing the position of vias on the "1" bits
|
||||
|
||||
# Record which bits need to be set
|
||||
bitflips = []
|
||||
for j in range(0, 13):
|
||||
if binval[12 - j] == '1':
|
||||
bitflips.append(j)
|
||||
|
||||
with open(magpath + '/gpio_defaults_block.mag', 'r') as ifile:
|
||||
maglines = ifile.read().splitlines()
|
||||
outlines = []
|
||||
for magline in maglines:
|
||||
is_flipped = False
|
||||
for bitflip in bitflips:
|
||||
if magline == zero_string[bitflip]:
|
||||
is_flipped = True
|
||||
break
|
||||
if is_flipped:
|
||||
outlines.append(one_string[bitflip])
|
||||
else:
|
||||
outlines.append(magline)
|
||||
|
||||
print('Creating new layout file ' + mag_file)
|
||||
if testmode:
|
||||
print('(Test only)')
|
||||
else:
|
||||
with open(mag_file, 'w') as ofile:
|
||||
for outline in outlines:
|
||||
print(outline, file=ofile)
|
||||
else:
|
||||
print('Layout file ' + mag_file + ' already exists and does not need to be generated.')
|
||||
|
||||
print('Step 2: Modify top-level layouts to use the specified defaults.')
|
||||
|
||||
# Create a backup of the caravan and caravel layouts
|
||||
if not testmode:
|
||||
shutil.copy(magpath + '/caravel.mag', magpath + '/caravel.mag.bak')
|
||||
shutil.copy(magpath + '/caravan.mag', magpath + '/caravan.mag.bak')
|
||||
|
||||
if testmode:
|
||||
print('Test only: Caravel layout:')
|
||||
with open(magpath + '/caravel.mag', 'r') as ifile:
|
||||
maglines = ifile.read().splitlines()
|
||||
outlines = []
|
||||
for magline in maglines:
|
||||
if magline.startswith('use '):
|
||||
tokens = magline.split()
|
||||
instname = tokens[2]
|
||||
if instname.startswith('gpio_defaults_block_'):
|
||||
gpioidx = instname[20:]
|
||||
cellname = cellsused[int(gpioidx)]
|
||||
if cellname:
|
||||
tokens[1] = cellname
|
||||
outlines.append(' '.join(tokens))
|
||||
if testmode:
|
||||
print('Replacing line: ' + magline)
|
||||
print('With: ' + ' '.join(tokens))
|
||||
else:
|
||||
outlines.append(magline)
|
||||
else:
|
||||
outlines.append(magline)
|
||||
|
||||
if not testmode:
|
||||
with open(magpath + '/caravel.mag', 'w') as ofile:
|
||||
for outline in outlines:
|
||||
print(outline, file=ofile)
|
||||
|
||||
if testmode:
|
||||
print('Test only: Caravan layout:')
|
||||
with open(magpath + '/caravan.mag', 'r') as ifile:
|
||||
maglines = ifile.read().splitlines()
|
||||
outlines = []
|
||||
for magline in maglines:
|
||||
if magline.startswith('use '):
|
||||
tokens = magline.split()
|
||||
instname = tokens[2]
|
||||
if instname.startswith('gpio_defaults_block_'):
|
||||
gpioidx = instname[20:]
|
||||
cellname = cellsused[int(gpioidx)]
|
||||
if cellname:
|
||||
tokens[1] = cellname
|
||||
outlines.append(' '.join(tokens))
|
||||
if testmode:
|
||||
print('Replacing line: ' + magline)
|
||||
print('With: ' + ' '.join(tokens))
|
||||
else:
|
||||
outlines.append(magline)
|
||||
else:
|
||||
outlines.append(magline)
|
||||
|
||||
if not testmode:
|
||||
with open(magpath + '/caravan.mag', 'w') as ofile:
|
||||
for outline in outlines:
|
||||
print(outline, file=ofile)
|
||||
|
||||
print('Done.')
|
||||
sys.exit(0)
|
|
@ -0,0 +1,416 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#
|
||||
# generate_fill.py ---
|
||||
#
|
||||
# Run the fill generation on a layout top level.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import glob
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
|
||||
def usage():
|
||||
print("Usage:")
|
||||
print("generate_fill.py <user_id_value> <project> <path_to_project> [-keep] [-test] [-dist]")
|
||||
print("")
|
||||
print("where:")
|
||||
print(" <user_id_value> is a character string of eight hex digits, and")
|
||||
print(" <path_to_project> is the path to the project top level directory.")
|
||||
print("")
|
||||
print(" If <user_id_value> is not given, then it must exist in the info.yaml file.")
|
||||
print(" If <path_to_project> is not given, then it is assumed to be the cwd.")
|
||||
print(" If '-keep' is specified, then keep the generation script.")
|
||||
print(" If '-test' is specified, then create but do not run the generation script.")
|
||||
print(" If '-dist' is specified, then run distributed (multi-processing).")
|
||||
|
||||
return 0
|
||||
|
||||
def makegds(file):
|
||||
# Procedure for multiprocessing run only: Run the distributed processing
|
||||
# script to load a .mag file of one flattened square area of the layout,
|
||||
# and run the fill generator to produce a .gds file output from it.
|
||||
|
||||
magpath = os.path.split(file)[0]
|
||||
filename = os.path.split(file)[1]
|
||||
|
||||
myenv = os.environ.copy()
|
||||
myenv['MAGTYPE'] = 'mag'
|
||||
|
||||
mproc = subprocess.run(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, magpath + '/generate_fill_dist.tcl',
|
||||
filename],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
if mproc.stdout:
|
||||
for line in mproc.stdout.splitlines():
|
||||
print(line)
|
||||
if mproc.stderr:
|
||||
print('Error message output from magic:')
|
||||
for line in mproc.stderr.splitlines():
|
||||
print(line)
|
||||
if mproc.returncode != 0:
|
||||
print('ERROR: Magic exited with status ' + str(mproc.returncode))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
optionlist = []
|
||||
arguments = []
|
||||
|
||||
debugmode = False
|
||||
keepmode = False
|
||||
testmode = False
|
||||
distmode = False
|
||||
|
||||
for option in sys.argv[1:]:
|
||||
if option.find('-', 0) == 0:
|
||||
optionlist.append(option)
|
||||
else:
|
||||
arguments.append(option)
|
||||
|
||||
if len(arguments) < 3:
|
||||
print("Wrong number of arguments given to generate_fill.py.")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
user_id_value = arguments[0]
|
||||
project = arguments[1]
|
||||
user_project_path = arguments[2]
|
||||
|
||||
try:
|
||||
# Convert to binary
|
||||
user_id_int = int('0x' + user_id_value, 0)
|
||||
user_id_bits = '{0:032b}'.format(user_id_int)
|
||||
|
||||
except:
|
||||
print("User ID not recognized")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
# if len(arguments) == 0:
|
||||
# user_project_path = os.getcwd()
|
||||
# elif len(arguments) == 2:
|
||||
# user_project_path = arguments[1]
|
||||
# elif user_project_path == None:
|
||||
# user_project_path = arguments[0]
|
||||
# else:
|
||||
# user_project_path = os.getcwd()
|
||||
|
||||
|
||||
if not os.path.isdir(user_project_path):
|
||||
print('Error: Project path "' + user_project_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid user ID
|
||||
# if not user_id_value:
|
||||
# if os.path.isfile(user_project_path + '/info.yaml'):
|
||||
# with open(user_project_path + '/info.yaml', 'r') as ifile:
|
||||
# infolines = ifile.read().splitlines()
|
||||
# for line in infolines:
|
||||
# kvpair = line.split(':')
|
||||
# if len(kvpair) == 2:
|
||||
# key = kvpair[0].strip()
|
||||
# value = kvpair[1].strip()
|
||||
# if key == 'project_id':
|
||||
# user_id_value = value.strip('"\'')
|
||||
# break
|
||||
|
||||
if user_id_value:
|
||||
project_with_id = 'caravel_' + user_id_value
|
||||
else:
|
||||
print('Error: No project_id found in info.yaml file.')
|
||||
sys.exit(1)
|
||||
|
||||
if '-debug' in optionlist:
|
||||
debugmode = True
|
||||
if '-keep' in optionlist:
|
||||
keepmode = True
|
||||
if '-test' in optionlist:
|
||||
testmode = True
|
||||
if '-dist' in optionlist:
|
||||
distmode = True
|
||||
|
||||
magpath = user_project_path + '/mag'
|
||||
rcfile = magpath + '/.magicrc'
|
||||
|
||||
if not os.path.isfile(rcfile):
|
||||
rcfile = None
|
||||
|
||||
topdir = user_project_path
|
||||
gdsdir = topdir + '/gds'
|
||||
hasgdsdir = True if os.path.isdir(gdsdir) else False
|
||||
|
||||
ofile = open(magpath + '/generate_fill.tcl', 'w')
|
||||
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
print('tech unlock *', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
print('box size 700um 700um', file=ofile)
|
||||
print('set stepbox [box values]', file=ofile)
|
||||
print('set stepwidth [lindex $stepbox 2]', file=ofile)
|
||||
print('set stepheight [lindex $stepbox 3]', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('set starttime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Started: $starttime"', file=ofile)
|
||||
print('', file=ofile)
|
||||
# Read the user project from GDS, as there is not necessarily a magic database file
|
||||
# to go along with this.
|
||||
# print('gds read ../gds/user_project_wrapper', file=ofile)
|
||||
# Now read the full caravel project
|
||||
# print('load ' + project + ' -dereference', file=ofile)
|
||||
print('gds readonly true', file=ofile)
|
||||
print('gds rescale false', file=ofile)
|
||||
print('gds read ../gds/' + project, file=ofile)
|
||||
print('select top cell', file=ofile)
|
||||
print('expand', file=ofile)
|
||||
if not distmode:
|
||||
print('cif ostyle wafflefill(tiled)', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('set fullbox [box values]', file=ofile)
|
||||
print('set xmax [lindex $fullbox 2]', file=ofile)
|
||||
print('set xmin [lindex $fullbox 0]', file=ofile)
|
||||
print('set fullwidth [expr {$xmax - $xmin}]', file=ofile)
|
||||
print('set xtiles [expr {int(ceil(($fullwidth + 0.0) / $stepwidth))}]', file=ofile)
|
||||
print('set ymax [lindex $fullbox 3]', file=ofile)
|
||||
print('set ymin [lindex $fullbox 1]', file=ofile)
|
||||
print('set fullheight [expr {$ymax - $ymin}]', file=ofile)
|
||||
print('set ytiles [expr {int(ceil(($fullheight + 0.0) / $stepheight))}]', file=ofile)
|
||||
print('box size $stepwidth $stepheight', file=ofile)
|
||||
print('set xbase [lindex $fullbox 0]', file=ofile)
|
||||
print('set ybase [lindex $fullbox 1]', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
# Break layout into tiles and process each separately
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' set xlo [expr $xbase + $x * $stepwidth]', file=ofile)
|
||||
print(' set ylo [expr $ybase + $y * $stepheight]', file=ofile)
|
||||
print(' set xhi [expr $xlo + $stepwidth]', file=ofile)
|
||||
print(' set yhi [expr $ylo + $stepheight]', file=ofile)
|
||||
print(' if {$xhi > $fullwidth} {set xhi $fullwidth}', file=ofile)
|
||||
print(' if {$yhi > $fullheight} {set yhi $fullheight}', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
# The flattened area must be larger than the fill tile by >1.5um
|
||||
print(' box grow c 1.6um', file=ofile)
|
||||
|
||||
# Flatten into a cell with a new name
|
||||
print(' puts stdout "Flattening layout of tile x=$x y=$y. . . "', file=ofile)
|
||||
print(' flush stdout', file=ofile)
|
||||
print(' update idletasks', file=ofile)
|
||||
print(' flatten -dobox -nolabels ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
print(' load ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
# Remove any GDS_FILE reference (there should not be any?)
|
||||
print(' property GDS_FILE ""', file=ofile)
|
||||
# Set boundary using comment layer, to the size of the step box
|
||||
# This corresponds to the "topbox" rule in the wafflefill(tiled) style
|
||||
print(' select top cell', file=ofile)
|
||||
print(' erase comment', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
print(' paint comment', file=ofile)
|
||||
|
||||
if not distmode:
|
||||
print(' puts stdout "Writing GDS. . . "', file=ofile)
|
||||
|
||||
print(' flush stdout', file=ofile)
|
||||
print(' update idletasks', file=ofile)
|
||||
|
||||
if distmode:
|
||||
print(' writeall force ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
else:
|
||||
print(' gds write ' + project_with_id + '_fill_pattern_${x}_$y.gds', file=ofile)
|
||||
# Reload project top
|
||||
print(' load ' + project, file=ofile)
|
||||
|
||||
# Remove last generated cell to save memory
|
||||
print(' cellname delete ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
if distmode:
|
||||
print('set ofile [open fill_gen_info.txt w]', file=ofile)
|
||||
print('puts $ofile "$stepwidth"', file=ofile)
|
||||
print('puts $ofile "$stepheight"', file=ofile)
|
||||
print('puts $ofile "$xtiles"', file=ofile)
|
||||
print('puts $ofile "$ytiles"', file=ofile)
|
||||
print('puts $ofile "$xbase"', file=ofile)
|
||||
print('puts $ofile "$ybase"', file=ofile)
|
||||
print('close $ofile', file=ofile)
|
||||
print('quit -noprompt', file=ofile)
|
||||
ofile.close()
|
||||
|
||||
with open(magpath + '/generate_fill_dist.tcl', 'w') as ofile:
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
print('tech unlock *', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
print('set filename [file root [lindex $argv $argc-1]]', file=ofile)
|
||||
print('load $filename', file=ofile)
|
||||
print('cif ostyle wafflefill(tiled)', file=ofile)
|
||||
print('gds write [file root $filename].gds', file=ofile)
|
||||
print('quit -noprompt', file=ofile)
|
||||
|
||||
ofile = open(magpath + '/generate_fill_final.tcl', 'w')
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
print('tech unlock *', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
|
||||
print('set ifile [open fill_gen_info.txt r]', file=ofile)
|
||||
print('gets $ifile stepwidth', file=ofile)
|
||||
print('gets $ifile stepheight', file=ofile)
|
||||
print('gets $ifile xtiles', file=ofile)
|
||||
print('gets $ifile ytiles', file=ofile)
|
||||
print('gets $ifile xbase', file=ofile)
|
||||
print('gets $ifile ybase', file=ofile)
|
||||
print('close $ifile', file=ofile)
|
||||
print('cif ostyle wafflefill(tiled)', file=ofile)
|
||||
|
||||
# Now create simple "fake" views of all the tiles.
|
||||
print('gds readonly true', file=ofile)
|
||||
print('gds rescale false', file=ofile)
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' set xlo [expr $xbase + $x * $stepwidth]', file=ofile)
|
||||
print(' set ylo [expr $ybase + $y * $stepheight]', file=ofile)
|
||||
print(' set xhi [expr $xlo + $stepwidth]', file=ofile)
|
||||
print(' set yhi [expr $ylo + $stepheight]', file=ofile)
|
||||
print(' load ' + project_with_id + '_fill_pattern_${x}_$y -quiet', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
print(' paint comment', file=ofile)
|
||||
print(' property FIXED_BBOX "$xlo $ylo $xhi $yhi"', file=ofile)
|
||||
print(' property GDS_FILE ' + project_with_id + '_fill_pattern_${x}_${y}.gds', file=ofile)
|
||||
print(' property GDS_START 0', file=ofile)
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
# Now tile everything back together
|
||||
print('load ' + project_with_id + '_fill_pattern -quiet', file=ofile)
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' box values 0 0 0 0', file=ofile)
|
||||
print(' getcell ' + project_with_id + '_fill_pattern_${x}_$y child 0 0', file=ofile)
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
# And write final GDS
|
||||
print('puts stdout "Writing final GDS"', file=ofile)
|
||||
|
||||
print('cif *hier write disable', file=ofile)
|
||||
print('cif *array write disable', file=ofile)
|
||||
if hasgdsdir:
|
||||
print('gds write ../gds/' + project_with_id + '_fill_pattern.gds', file=ofile)
|
||||
else:
|
||||
print('gds write ' + project_with_id + '_fill_pattern.gds', file=ofile)
|
||||
print('set endtime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Ended: $endtime"', file=ofile)
|
||||
print('quit -noprompt', file=ofile)
|
||||
ofile.close()
|
||||
|
||||
myenv = os.environ.copy()
|
||||
myenv['MAGTYPE'] = 'mag'
|
||||
|
||||
if not testmode:
|
||||
# Diagnostic
|
||||
# print('This script will generate file ' + project_with_id + '_fill_pattern.gds')
|
||||
print('This script will generate files ' + project_with_id + '_fill_pattern_x_y.gds')
|
||||
print('Now generating fill patterns. This may take. . . quite. . . a while.', flush=True)
|
||||
mproc = subprocess.run(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, magpath + '/generate_fill.tcl'],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
if mproc.stdout:
|
||||
for line in mproc.stdout.splitlines():
|
||||
print(line)
|
||||
if mproc.stderr:
|
||||
print('Error message output from magic:')
|
||||
for line in mproc.stderr.splitlines():
|
||||
print(line)
|
||||
if mproc.returncode != 0:
|
||||
print('ERROR: Magic exited with status ' + str(mproc.returncode))
|
||||
|
||||
if distmode:
|
||||
# If using distributed mode, then run magic on each of the generated
|
||||
# layout files
|
||||
pool = multiprocessing.Pool()
|
||||
magfiles = glob.glob(magpath + '/' + project_with_id + '_fill_pattern_*.mag')
|
||||
# NOTE: Adding 'x' to the end of each filename, or else magic will
|
||||
# try to read it from the command line as well as passing it as an
|
||||
# argument to the script. We only want it passed as an argument.
|
||||
magxfiles = list(item + 'x' for item in magfiles)
|
||||
pool.map(makegds, magxfiles)
|
||||
|
||||
# If using distributed mode, then remove all of the temporary .mag files
|
||||
# and then run the final generation script.
|
||||
for file in magfiles:
|
||||
os.remove(file)
|
||||
|
||||
mproc = subprocess.run(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, magpath + '/generate_fill_final.tcl'],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
if mproc.stdout:
|
||||
for line in mproc.stdout.splitlines():
|
||||
print(line)
|
||||
if mproc.stderr:
|
||||
print('Error message output from magic:')
|
||||
for line in mproc.stderr.splitlines():
|
||||
print(line)
|
||||
if mproc.returncode != 0:
|
||||
print('ERROR: Magic exited with status ' + str(mproc.returncode))
|
||||
|
||||
if not keepmode:
|
||||
# Remove fill generation script
|
||||
os.remove(magpath + '/generate_fill.tcl')
|
||||
# Remove all individual fill tiles, leaving only the composite GDS.
|
||||
filelist = os.listdir(magpath)
|
||||
for file in filelist:
|
||||
if os.path.splitext(magpath + '/' + file)[1] == '.gds':
|
||||
if file.startswith(project_with_id + '_fill_pattern_'):
|
||||
os.remove(magpath + '/' + file)
|
||||
|
||||
if distmode:
|
||||
os.remove(magpath + '/generate_fill_dist.tcl')
|
||||
os.remove(magpath + '/generate_fill_final.tcl')
|
||||
os.remove(magpath + '/fill_gen_info.txt')
|
||||
if testmode:
|
||||
magfiles = glob.glob(magpath + '/' + project_with_id + '_fill_pattern_*.mag')
|
||||
for file in magfiles:
|
||||
os.remove(file)
|
||||
|
||||
print('Done!')
|
||||
exit(0)
|
|
@ -0,0 +1,268 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#
|
||||
# generate_fill_orig.py ---
|
||||
#
|
||||
# Run the fill generation on a layout top level.
|
||||
# This is the older version that does not have a "-dist" option for
|
||||
# distributed (multiprocessing) operation.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
def usage():
|
||||
print("Usage:")
|
||||
print("generate_fill_orig.py [<path_to_project>] [-keep] [-test]")
|
||||
print("")
|
||||
print("where:")
|
||||
print(" <path_to_project> is the path to the project top level directory.")
|
||||
print("")
|
||||
print(" If <path_to_project> is not given, then it is assumed to be the cwd.")
|
||||
print(" If '-keep' is specified, then keep the generation script.")
|
||||
print(" If '-test' is specified, then create but do not run the generation script.")
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
optionlist = []
|
||||
arguments = []
|
||||
|
||||
debugmode = False
|
||||
keepmode = False
|
||||
testmode = False
|
||||
|
||||
for option in sys.argv[1:]:
|
||||
if option.find('-', 0) == 0:
|
||||
optionlist.append(option)
|
||||
else:
|
||||
arguments.append(option)
|
||||
|
||||
if len(arguments) > 1:
|
||||
print("Wrong number of arguments given to generate_fill_orig.py.")
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if len(arguments) == 1:
|
||||
user_project_path = arguments[0]
|
||||
else:
|
||||
user_project_path = os.getcwd()
|
||||
|
||||
if not os.path.isdir(user_project_path):
|
||||
print('Error: Project path "' + user_project_path + '" does not exist or is not readable.')
|
||||
sys.exit(1)
|
||||
|
||||
# Check for valid user ID
|
||||
user_id_value = None
|
||||
if os.path.isfile(user_project_path + '/info.yaml'):
|
||||
with open(user_project_path + '/info.yaml', 'r') as ifile:
|
||||
infolines = ifile.read().splitlines()
|
||||
for line in infolines:
|
||||
kvpair = line.split(':')
|
||||
if len(kvpair) == 2:
|
||||
key = kvpair[0].strip()
|
||||
value = kvpair[1].strip()
|
||||
if key == 'project_id':
|
||||
user_id_value = value.strip('"\'')
|
||||
break
|
||||
|
||||
project = 'caravel'
|
||||
if user_id_value:
|
||||
project_with_id = project + '_' + user_id_value
|
||||
else:
|
||||
print('Error: No project_id found in info.yaml file.')
|
||||
sys.exit(1)
|
||||
|
||||
if '-debug' in optionlist:
|
||||
debugmode = True
|
||||
if '-keep' in optionlist:
|
||||
keepmode = True
|
||||
if '-test' in optionlist:
|
||||
testmode = True
|
||||
|
||||
magpath = user_project_path + '/mag'
|
||||
rcfile = magpath + '/.magicrc'
|
||||
|
||||
if not os.path.isfile(rcfile):
|
||||
rcfile = None
|
||||
|
||||
topdir = user_project_path
|
||||
gdsdir = topdir + '/gds'
|
||||
hasgdsdir = True if os.path.isdir(gdsdir) else False
|
||||
|
||||
with open(magpath + '/generate_fill.tcl', 'w') as ofile:
|
||||
print('#!/bin/env wish', file=ofile)
|
||||
print('drc off', file=ofile)
|
||||
print('tech unlock *', file=ofile)
|
||||
print('snap internal', file=ofile)
|
||||
print('box values 0 0 0 0', file=ofile)
|
||||
print('box size 700um 700um', file=ofile)
|
||||
print('set stepbox [box values]', file=ofile)
|
||||
print('set stepwidth [lindex $stepbox 2]', file=ofile)
|
||||
print('set stepheight [lindex $stepbox 3]', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('set starttime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Started: $starttime"', file=ofile)
|
||||
print('', file=ofile)
|
||||
# Read the user project from GDS, as there is not necessarily a magic database file
|
||||
# to go along with this.
|
||||
# print('gds read ../gds/user_project_wrapper', file=ofile)
|
||||
# Now read the full caravel project
|
||||
# print('load ' + project + ' -dereference', file=ofile)
|
||||
print('gds readonly true', file=ofile)
|
||||
print('gds rescale false', file=ofile)
|
||||
print('gds read ../gds/caravel', file=ofile)
|
||||
print('select top cell', file=ofile)
|
||||
print('expand', file=ofile)
|
||||
print('cif ostyle wafflefill(tiled)', file=ofile)
|
||||
print('', file=ofile)
|
||||
print('set fullbox [box values]', file=ofile)
|
||||
print('set xmax [lindex $fullbox 2]', file=ofile)
|
||||
print('set xmin [lindex $fullbox 0]', file=ofile)
|
||||
print('set fullwidth [expr {$xmax - $xmin}]', file=ofile)
|
||||
print('set xtiles [expr {int(ceil(($fullwidth + 0.0) / $stepwidth))}]', file=ofile)
|
||||
print('set ymax [lindex $fullbox 3]', file=ofile)
|
||||
print('set ymin [lindex $fullbox 1]', file=ofile)
|
||||
print('set fullheight [expr {$ymax - $ymin}]', file=ofile)
|
||||
print('set ytiles [expr {int(ceil(($fullheight + 0.0) / $stepheight))}]', file=ofile)
|
||||
print('box size $stepwidth $stepheight', file=ofile)
|
||||
print('set xbase [lindex $fullbox 0]', file=ofile)
|
||||
print('set ybase [lindex $fullbox 1]', file=ofile)
|
||||
print('', file=ofile)
|
||||
|
||||
# Break layout into tiles and process each separately
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' set xlo [expr $xbase + $x * $stepwidth]', file=ofile)
|
||||
print(' set ylo [expr $ybase + $y * $stepheight]', file=ofile)
|
||||
print(' set xhi [expr $xlo + $stepwidth]', file=ofile)
|
||||
print(' set yhi [expr $ylo + $stepheight]', file=ofile)
|
||||
print(' if {$xhi > $fullwidth} {set xhi $fullwidth}', file=ofile)
|
||||
print(' if {$yhi > $fullheight} {set yhi $fullheight}', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
# The flattened area must be larger than the fill tile by >1.5um
|
||||
print(' box grow c 1.6um', file=ofile)
|
||||
|
||||
# Flatten into a cell with a new name
|
||||
print(' puts stdout "Flattening layout of tile x=$x y=$y. . . "', file=ofile)
|
||||
print(' flush stdout', file=ofile)
|
||||
print(' update idletasks', file=ofile)
|
||||
print(' flatten -dobox -nolabels ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
print(' load ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
|
||||
# Remove any GDS_FILE reference (there should not be any?)
|
||||
print(' property GDS_FILE ""', file=ofile)
|
||||
# Set boundary using comment layer, to the size of the step box
|
||||
# This corresponds to the "topbox" rule in the wafflefill(tiled) style
|
||||
print(' select top cell', file=ofile)
|
||||
print(' erase comment', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
print(' paint comment', file=ofile)
|
||||
print(' puts stdout "Writing GDS. . . "', file=ofile)
|
||||
print(' flush stdout', file=ofile)
|
||||
print(' update idletasks', file=ofile)
|
||||
print(' gds write ' + project_with_id + '_fill_pattern_${x}_$y.gds', file=ofile)
|
||||
|
||||
# Reload project top
|
||||
print(' load ' + project, file=ofile)
|
||||
|
||||
# Remove last generated cell to save memory
|
||||
print(' cellname delete ' + project_with_id + '_fill_pattern_${x}_$y', file=ofile)
|
||||
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
# Now create simple "fake" views of all the tiles.
|
||||
print('gds readonly true', file=ofile)
|
||||
print('gds rescale false', file=ofile)
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' set xlo [expr $xbase + $x * $stepwidth]', file=ofile)
|
||||
print(' set ylo [expr $ybase + $y * $stepheight]', file=ofile)
|
||||
print(' set xhi [expr $xlo + $stepwidth]', file=ofile)
|
||||
print(' set yhi [expr $ylo + $stepheight]', file=ofile)
|
||||
print(' load ' + project_with_id + '_fill_pattern_${x}_$y -quiet', file=ofile)
|
||||
print(' box values $xlo $ylo $xhi $yhi', file=ofile)
|
||||
print(' paint comment', file=ofile)
|
||||
print(' property FIXED_BBOX "$xlo $ylo $xhi $yhi"', file=ofile)
|
||||
print(' property GDS_FILE ' + project_with_id + '_fill_pattern_${x}_${y}.gds', file=ofile)
|
||||
print(' property GDS_START 0', file=ofile)
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
# Now tile everything back together
|
||||
print('load ' + project_with_id + '_fill_pattern -quiet', file=ofile)
|
||||
print('for {set y 0} {$y < $ytiles} {incr y} {', file=ofile)
|
||||
print(' for {set x 0} {$x < $xtiles} {incr x} {', file=ofile)
|
||||
print(' box values 0 0 0 0', file=ofile)
|
||||
print(' getcell ' + project_with_id + '_fill_pattern_${x}_$y child 0 0', file=ofile)
|
||||
print(' }', file=ofile)
|
||||
print('}', file=ofile)
|
||||
|
||||
# And write final GDS
|
||||
print('puts stdout "Writing final GDS"', file=ofile)
|
||||
|
||||
print('cif *hier write disable', file=ofile)
|
||||
print('cif *array write disable', file=ofile)
|
||||
if hasgdsdir:
|
||||
print('gds write ../gds/' + project_with_id + '_fill_pattern.gds', file=ofile)
|
||||
else:
|
||||
print('gds write ' + project_with_id + '_fill_pattern.gds', file=ofile)
|
||||
print('set endtime [orig_clock format [orig_clock seconds] -format "%D %T"]', file=ofile)
|
||||
print('puts stdout "Ended: $endtime"', file=ofile)
|
||||
print('quit -noprompt', file=ofile)
|
||||
|
||||
myenv = os.environ.copy()
|
||||
myenv['MAGTYPE'] = 'mag'
|
||||
|
||||
if not testmode:
|
||||
# Diagnostic
|
||||
# print('This script will generate file ' + project_with_id + '_fill_pattern.gds')
|
||||
print('This script will generate files ' + project_with_id + '_fill_pattern_x_y.gds')
|
||||
print('Now generating fill patterns. This may take. . . quite. . . a while.', flush=True)
|
||||
mproc = subprocess.run(['magic', '-dnull', '-noconsole',
|
||||
'-rcfile', rcfile, magpath + '/generate_fill.tcl'],
|
||||
stdin = subprocess.DEVNULL,
|
||||
stdout = subprocess.PIPE,
|
||||
stderr = subprocess.PIPE,
|
||||
cwd = magpath,
|
||||
env = myenv,
|
||||
universal_newlines = True)
|
||||
if mproc.stdout:
|
||||
for line in mproc.stdout.splitlines():
|
||||
print(line)
|
||||
if mproc.stderr:
|
||||
print('Error message output from magic:')
|
||||
for line in mproc.stderr.splitlines():
|
||||
print(line)
|
||||
if mproc.returncode != 0:
|
||||
print('ERROR: Magic exited with status ' + str(mproc.returncode))
|
||||
|
||||
if not keepmode:
|
||||
# Remove fill generation script
|
||||
os.remove(magpath + '/generate_fill.tcl')
|
||||
# Remove all individual fill tiles, leaving only the composite GDS.
|
||||
filelist = os.listdir(magpath)
|
||||
for file in filelist:
|
||||
if os.path.splitext(magpath + '/' + file)[1] == '.gds':
|
||||
if file.startswith(project + '_fill_pattern_'):
|
||||
os.remove(magpath + '/' + file)
|
||||
|
||||
print('Done!')
|
||||
exit(0)
|
|
@ -0,0 +1,702 @@
|
|||
# SPDX-FileCopyrightText: 2020 Efabless Corporation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#----------------------------------------------------------------------
|
||||
# Assumes running magic -T micross using the micross technology file
|
||||
# from the open_pdks installation of sky130A
|
||||
#----------------------------------------------------------------------
|
||||
# bump bond pitch is 500um. Bump diameter is set by the technology
|
||||
|
||||
namespace path {::tcl::mathop ::tcl::mathfunc}
|
||||
|
||||
if {[catch {set PDKPATH $env(PDKPATH)}]} {
|
||||
set PDKPATH "$::env(PDK_ROOT)/sky130A"
|
||||
}
|
||||
|
||||
source $PDKPATH/libs.tech/magic/current/bump_bond_generator/bump_bond.tcl
|
||||
|
||||
# Caravel dimensions, in microns
|
||||
set chipwidth 3588
|
||||
set chipheight 5188
|
||||
|
||||
set halfwidth [/ $chipwidth 2]
|
||||
set halfheight [/ $chipheight 2]
|
||||
|
||||
set columns 6
|
||||
set rows 10
|
||||
|
||||
set bump_pitch 500
|
||||
|
||||
set llx [- $halfwidth [* [- [/ $columns 2] 0.5] $bump_pitch]]
|
||||
set lly [- $halfheight [* [- [/ $rows 2] 0.5] $bump_pitch]]
|
||||
|
||||
# Create a new cell
|
||||
load caravel_bump_bond -quiet
|
||||
|
||||
# Build the bump cells
|
||||
make_bump_bond 0
|
||||
make_bump_bond 45
|
||||
|
||||
# View the whole chip during generation. This is not strictly
|
||||
# necessary, but looks nice!
|
||||
snap internal
|
||||
box values 0 0 ${chipwidth}um ${chipheight}um
|
||||
paint glass
|
||||
view
|
||||
erase glass
|
||||
box values 0 0 0 0
|
||||
grid 250um 250um 45um 95um
|
||||
|
||||
# Starting from the bottom left-hand corner and scanning across and up,
|
||||
# these are the orientations of the bump bond pad tapers:
|
||||
set tapers {}
|
||||
lappend tapers 180 225 270 270 270 270
|
||||
lappend tapers 180 135 225 270 0 0
|
||||
lappend tapers 180 135 135 270 315 0
|
||||
lappend tapers 180 135 135 315 315 0
|
||||
lappend tapers 135 135 0 180 315 0
|
||||
lappend tapers 180 135 0 180 315 0
|
||||
lappend tapers 180 135 180 315 315 0
|
||||
lappend tapers 180 180 135 45 315 0
|
||||
lappend tapers 135 135 135 45 45 45
|
||||
lappend tapers 90 90 90 90 45 90
|
||||
|
||||
box values 0 0 0 0
|
||||
set t 0
|
||||
for {set y 0} {$y < $rows} {incr y} {
|
||||
for {set x 0} {$x < $columns} {incr x} {
|
||||
set xpos [+ $llx [* $x $bump_pitch]]
|
||||
set ypos [+ $lly [* $y $bump_pitch]]
|
||||
draw_bump_bond $xpos $ypos [lindex $tapers $t]
|
||||
incr t
|
||||
}
|
||||
}
|
||||
|
||||
# The pad at E6 has wires exiting two sides, so put another pad down
|
||||
# at the other orientation.
|
||||
set y 4
|
||||
set x 4
|
||||
set xpos [+ $llx [* $x $bump_pitch]]
|
||||
set ypos [+ $lly [* $y $bump_pitch]]
|
||||
draw_bump_bond $xpos $ypos 180
|
||||
|
||||
select top cell
|
||||
expand
|
||||
|
||||
# These are the pad Y positions on the left side from bottom to top
|
||||
|
||||
set leftpads {}
|
||||
lappend leftpads 377.5 588.5 950.5 1166.5 1382.5 1598.5 1814.5
|
||||
lappend leftpads 2030.5 2241.5 2452.5 2668.5 2884.5 3100.5
|
||||
lappend leftpads 3316.5 3532.5 3748.5 3964.5 4175.5 4386.5 4597.5 4813.5
|
||||
|
||||
# These are the pad X positions on the top side from left to right
|
||||
|
||||
set toppads {}
|
||||
lappend toppads 423.5 680.5 937.5 1194.5 1452.5 1704.5 1961.5 2406.5
|
||||
lappend toppads 2663.5 2915.5 3172.5
|
||||
|
||||
# These are the pad Y positions on the right side from bottom to top
|
||||
|
||||
set rightpads {}
|
||||
lappend rightpads 537.5 763.5 988.5 1214.5 1439.5 1664.5 1890.5
|
||||
lappend rightpads 2115.5 2336.5 2556.5 2776.5 3002.5 3227.5 3453.5
|
||||
lappend rightpads 3678.5 3903.5 4129.5 4349.5 4575.5 4795.5
|
||||
|
||||
# These are the pad X positions on the bottom side from left to right
|
||||
|
||||
set bottompads {}
|
||||
lappend bottompads 431.5 700.5 969.5 1243.5 1512.5 1786.5 2060.5
|
||||
lappend bottompads 2334.5 2608.5 2882.5 3151.5
|
||||
|
||||
set leftpadx 64.6
|
||||
set rightpadx 3523.78
|
||||
set bottompady 64.6
|
||||
set toppady 5123.78
|
||||
|
||||
set xpos $leftpadx
|
||||
for {set y 0} {$y < [llength $leftpads]} {incr y} {
|
||||
set ypos [lindex $leftpads $y]
|
||||
draw_pad_bond $xpos $ypos
|
||||
}
|
||||
|
||||
set ypos $toppady
|
||||
for {set x 0} {$x < [llength $toppads]} {incr x} {
|
||||
set xpos [lindex $toppads $x]
|
||||
draw_pad_bond $xpos $ypos
|
||||
}
|
||||
|
||||
set xpos $rightpadx
|
||||
for {set y 0} {$y < [llength $rightpads]} {incr y} {
|
||||
set ypos [lindex $rightpads $y]
|
||||
draw_pad_bond $xpos $ypos
|
||||
}
|
||||
|
||||
set ypos $bottompady
|
||||
for {set x 0} {$x < [llength $bottompads]} {incr x} {
|
||||
set xpos [lindex $bottompads $x]
|
||||
draw_pad_bond $xpos $ypos
|
||||
}
|
||||
|
||||
# Now route between the wirebond pads and the bump bond pads
|
||||
# routes start centered on the wirebond pad and align to grid points
|
||||
# on a 1/2 ball pitch, although positions do not need to be on
|
||||
# integer values. The overlaid grid starts 1/2 pitch to the left
|
||||
# and below the center of the bottom left bump bond. Grid columns
|
||||
# are numbered 0 to 12, and grid rows are numbered 0 to 20. To
|
||||
# convert to a micron unit coordinate, use the to_grid procedure
|
||||
# defined below.
|
||||
|
||||
set gridllx [- $llx 250.0]
|
||||
set gridlly [- $lly 250.0]
|
||||
set gridpitchx 250.0
|
||||
set gridpitchy 250.0
|
||||
|
||||
proc to_grid {x y} {
|
||||
global gridllx gridlly
|
||||
set coords []
|
||||
catch {lappend coords [+ $gridllx [* 250.0 $x]]}
|
||||
catch {lappend coords [+ $gridlly [* 250.0 $y]]}
|
||||
return $coords
|
||||
}
|
||||
|
||||
# Detailed routing, scanning left to right and from bottom to top.
|
||||
# (This really needs to be automated. . .)
|
||||
|
||||
set wire_width 40.0
|
||||
|
||||
# A10 vccd
|
||||
set coords [list $leftpadx [lindex $leftpads 0]]
|
||||
lappend coords {*}[to_grid -0.8 1]
|
||||
lappend coords {*}[to_grid 1 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B10 resetb
|
||||
set coords [list [lindex $bottompads 1] $bottompady]
|
||||
lappend coords {*}[to_grid 1.9 0.2]
|
||||
lappend coords {*}[to_grid 2.2 0.2]
|
||||
lappend coords {*}[to_grid 3 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C10 flash csb
|
||||
set coords [list [lindex $bottompads 4] $bottompady]
|
||||
lappend coords {*}[to_grid 5 0]
|
||||
lappend coords {*}[to_grid 5 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D10 flash io0
|
||||
set coords [list [lindex $bottompads 6] $bottompady]
|
||||
lappend coords {*}[to_grid 7 0]
|
||||
lappend coords {*}[to_grid 7 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E10 gpio
|
||||
set coords [list [lindex $bottompads 8] $bottompady]
|
||||
lappend coords {*}[to_grid 9 0.2]
|
||||
lappend coords {*}[to_grid 9 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F10 vdda
|
||||
set coords [list [lindex $bottompads 10] $bottompady]
|
||||
lappend coords {*}[to_grid 11 0.3]
|
||||
lappend coords {*}[to_grid 11 1]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A9 mprj_io[37]
|
||||
set coords [list $leftpadx [lindex $leftpads 2]]
|
||||
lappend coords {*}[to_grid -0.5 3]
|
||||
lappend coords {*}[to_grid 1 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B9 mprj_io[36]
|
||||
set coords [list $leftpadx [lindex $leftpads 3]]
|
||||
lappend coords {*}[to_grid -0.6 4]
|
||||
lappend coords {*}[to_grid 2 4]
|
||||
lappend coords {*}[to_grid 3 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C9 clock
|
||||
set coords [list [lindex $bottompads 2] $bottompady]
|
||||
lappend coords {*}[to_grid 3 0.2]
|
||||
lappend coords {*}[to_grid 3.4 0.2]
|
||||
lappend coords {*}[to_grid 3.8 0.6]
|
||||
lappend coords {*}[to_grid 3.8 1.6]
|
||||
lappend coords {*}[to_grid 4.5 2.3]
|
||||
lappend coords {*}[to_grid 4.5 2.5]
|
||||
lappend coords {*}[to_grid 5 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D9 flash io1
|
||||
set coords [list [lindex $bottompads 7] $bottompady]
|
||||
lappend coords {*}[to_grid 8 0.1]
|
||||
lappend coords {*}[to_grid 8 1.3]
|
||||
lappend coords {*}[to_grid 7 2.3]
|
||||
lappend coords {*}[to_grid 7 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E9 mprj_io[1]/SDO
|
||||
set coords [list $rightpadx [lindex $rightpads 1]]
|
||||
lappend coords {*}[to_grid 12.4 2.2]
|
||||
lappend coords {*}[to_grid 10.5 2.2]
|
||||
lappend coords {*}[to_grid 9.7 3]
|
||||
lappend coords {*}[to_grid 9 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F9 mprj_io[2]/SDI
|
||||
set coords [list $rightpadx [lindex $rightpads 2]]
|
||||
lappend coords {*}[to_grid 12.3 3]
|
||||
lappend coords {*}[to_grid 11 3]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A8 mprj_io[35]
|
||||
set coords [list $leftpadx [lindex $leftpads 4]]
|
||||
lappend coords {*}[to_grid -0.7 5]
|
||||
lappend coords {*}[to_grid 1 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B8 mprj_io[34]
|
||||
set coords [list $leftpadx [lindex $leftpads 5]]
|
||||
lappend coords {*}[to_grid -0.7 5.8]
|
||||
lappend coords {*}[to_grid 2.2 5.8]
|
||||
lappend coords {*}[to_grid 3 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C8 mprj_io[33]
|
||||
set coords [list $leftpadx [lindex $leftpads 6]]
|
||||
lappend coords {*}[to_grid -0.3 6.2]
|
||||
lappend coords {*}[to_grid 3.8 6.2]
|
||||
lappend coords {*}[to_grid 5 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D8 flash clk
|
||||
set coords [list [lindex $bottompads 5] $bottompady]
|
||||
lappend coords {*}[to_grid 6 0]
|
||||
lappend coords {*}[to_grid 6 1]
|
||||
lappend coords {*}[to_grid 6.2 1.2]
|
||||
lappend coords {*}[to_grid 6.2 3.5]
|
||||
lappend coords {*}[to_grid 7 4.3]
|
||||
lappend coords {*}[to_grid 7 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E8 mprj_io[3]/CSB
|
||||
set coords [list $rightpadx [lindex $rightpads 3]]
|
||||
lappend coords {*}[to_grid 12.4 4]
|
||||
lappend coords {*}[to_grid 10 4]
|
||||
lappend coords {*}[to_grid 9 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F8 mrpj_io[4]/SCK
|
||||
set coords [list $rightpadx [lindex $rightpads 4]]
|
||||
lappend coords {*}[to_grid 12.5 5]
|
||||
lappend coords {*}[to_grid 11 5]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A7 mrpj_io[32]
|
||||
set coords [list $leftpadx [lindex $leftpads 7]]
|
||||
lappend coords {*}[to_grid -0.2 7]
|
||||
lappend coords {*}[to_grid 1 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B7 vssd2
|
||||
set coords [list $leftpadx [lindex $leftpads 8]]
|
||||
lappend coords {*}[to_grid -0.1 7.8]
|
||||
lappend coords {*}[to_grid 2.2 7.8]
|
||||
lappend coords {*}[to_grid 3 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C7 vdda2
|
||||
set coords [list $leftpadx [lindex $leftpads 9]]
|
||||
lappend coords {*}[to_grid 0.3 8.2]
|
||||
lappend coords {*}[to_grid 2.3 8.2]
|
||||
lappend coords {*}[to_grid 2.5 8]
|
||||
lappend coords {*}[to_grid 4 8]
|
||||
lappend coords {*}[to_grid 5 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D7 mrpj_io[0]/JTAG
|
||||
set coords [list $rightpadx [lindex $rightpads 0]]
|
||||
lappend coords {*}[to_grid 12.8 1.8]
|
||||
lappend coords {*}[to_grid 10.2 1.8]
|
||||
lappend coords {*}[to_grid 9.8 2.2]
|
||||
lappend coords {*}[to_grid 8.6 2.2]
|
||||
lappend coords {*}[to_grid 8.2 2.6]
|
||||
lappend coords {*}[to_grid 8.2 5.8]
|
||||
lappend coords {*}[to_grid 7 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E7 mrpj_io[5]/ser_rx
|
||||
set coords [list $rightpadx [lindex $rightpads 5]]
|
||||
lappend coords {*}[to_grid 12.6 6]
|
||||
lappend coords {*}[to_grid 10 6]
|
||||
lappend coords {*}[to_grid 9 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F7 mprj_io[6]/ser_tx
|
||||
set coords [list $rightpadx [lindex $rightpads 6]]
|
||||
lappend coords {*}[to_grid 12.7 7]
|
||||
lappend coords {*}[to_grid 11 7]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A6 mprj_io[31]
|
||||
set coords [list $leftpadx [lindex $leftpads 10]]
|
||||
lappend coords {*}[to_grid -0.3 10.3]
|
||||
lappend coords {*}[to_grid 1 9]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B6 mprj_io[30]
|
||||
set coords [list $leftpadx [lindex $leftpads 11]]
|
||||
lappend coords {*}[to_grid -0.5 10.8]
|
||||
lappend coords {*}[to_grid -0.3 10.8]
|
||||
lappend coords {*}[to_grid 0.5 10]
|
||||
lappend coords {*}[to_grid 2 10]
|
||||
lappend coords {*}[to_grid 3 9]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C6 vssio/vssa/vssd: Connects to D6, D5, C5
|
||||
set coords [to_grid 5 9]
|
||||
lappend coords {*}[to_grid 5.65 9]
|
||||
lappend coords {*}[to_grid 5.85 9.2]
|
||||
lappend coords {*}[to_grid 6 9.2]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D6 vssio/vssa/vssd
|
||||
set coords [to_grid 7 9]
|
||||
lappend coords {*}[to_grid 6.35 9]
|
||||
lappend coords {*}[to_grid 6.15 8.8]
|
||||
lappend coords {*}[to_grid 6 8.8]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D6 vssio/vssa/vssd also goes to:
|
||||
set coords [list [lindex $bottompads 0] $bottompady]
|
||||
lappend coords {*}[to_grid 0.9 0.2]
|
||||
lappend coords {*}[to_grid 1.3 0.2]
|
||||
lappend coords {*}[to_grid 2 0.9]
|
||||
lappend coords {*}[to_grid 2 1.5]
|
||||
lappend coords {*}[to_grid 2.3 1.8]
|
||||
lappend coords {*}[to_grid 3.5 1.8]
|
||||
lappend coords {*}[to_grid 4.2 2.5]
|
||||
lappend coords {*}[to_grid 4.2 3.5]
|
||||
lappend coords {*}[to_grid 4.5 3.8]
|
||||
lappend coords {*}[to_grid 5.3 3.8]
|
||||
lappend coords {*}[to_grid 5.8 3.3]
|
||||
lappend coords {*}[to_grid 5.8 2.5]
|
||||
lappend coords {*}[to_grid 5.3 2]
|
||||
lappend coords {*}[to_grid 4.8 2]
|
||||
lappend coords {*}[to_grid 4.2 1.4]
|
||||
lappend coords {*}[to_grid 4.2 0.3]
|
||||
lappend coords {*}[list [lindex $bottompads 3] $bottompady]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D6 vssio/vssa/vssd also goes to:
|
||||
set coords [list [lindex $bottompads 9] $bottompady]
|
||||
lappend coords {*}[to_grid 10 0.3]
|
||||
lappend coords {*}[to_grid 10 1.4]
|
||||
lappend coords {*}[to_grid 9.6 1.8]
|
||||
lappend coords {*}[to_grid 8.5 1.8]
|
||||
lappend coords {*}[to_grid 7.8 2.5]
|
||||
lappend coords {*}[to_grid 7.8 5.5]
|
||||
lappend coords {*}[to_grid 7.3 6]
|
||||
lappend coords {*}[to_grid 6.2 6]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D6 vssio/vssa/vssd also goes to:
|
||||
set coords [list [lindex $toppads 5] $toppady]
|
||||
lappend coords {*}[to_grid 6 19.7]
|
||||
lappend coords {*}[to_grid 6 16]
|
||||
lappend coords {*}[to_grid 5.8 15.8]
|
||||
lappend coords {*}[to_grid 5.8 12.2]
|
||||
lappend coords {*}[to_grid 6 12]
|
||||
lappend coords {*}[to_grid 6 8]
|
||||
lappend coords {*}[to_grid 6.2 7.8]
|
||||
lappend coords {*}[to_grid 6.2 4.3]
|
||||
lappend coords {*}[to_grid 5.5 3.6]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E6 vssa1
|
||||
set coords [list $rightpadx [lindex $rightpads 7]]
|
||||
lappend coords {*}[to_grid 12.8 8]
|
||||
lappend coords {*}[to_grid 10 8]
|
||||
lappend coords {*}[to_grid 9 9]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E6 vssa1 also goes to
|
||||
set coords [list [lindex $toppads 9] $toppady]
|
||||
lappend coords {*}[to_grid 10 19.5]
|
||||
lappend coords {*}[to_grid 10 18.5]
|
||||
lappend coords {*}[to_grid 9.5 18]
|
||||
lappend coords {*}[to_grid 8.5 18]
|
||||
lappend coords {*}[to_grid 8 17.5]
|
||||
lappend coords {*}[to_grid 8 16.5]
|
||||
lappend coords {*}[to_grid 7.5 16]
|
||||
lappend coords {*}[to_grid 6.7 16]
|
||||
lappend coords {*}[to_grid 6.2 15.5]
|
||||
lappend coords {*}[to_grid 6.2 12.6]
|
||||
lappend coords {*}[to_grid 6.7 12]
|
||||
lappend coords {*}[to_grid 7.3 12]
|
||||
lappend coords {*}[to_grid 7.8 11.5]
|
||||
lappend coords {*}[to_grid 7.8 10.2]
|
||||
lappend coords {*}[to_grid 8 10]
|
||||
lappend coords {*}[to_grid 8 9.3]
|
||||
lappend coords {*}[to_grid 8.3 9]
|
||||
lappend coords {*}[to_grid 9 9]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F6 vssd1
|
||||
set coords [list $rightpadx [lindex $rightpads 8]]
|
||||
lappend coords {*}[to_grid 12.9 9]
|
||||
lappend coords {*}[to_grid 11 9]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A5 mprj_io[29]
|
||||
set coords [list $leftpadx [lindex $leftpads 12]]
|
||||
lappend coords {*}[to_grid 0.2 11]
|
||||
lappend coords {*}[to_grid 1 11]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B5 mprj_io[28]
|
||||
set coords [list $leftpadx [lindex $leftpads 13]]
|
||||
lappend coords {*}[to_grid 0 12]
|
||||
lappend coords {*}[to_grid 2 12]
|
||||
lappend coords {*}[to_grid 3 11]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C5 vssio/vssa/vssd : Connects to D6, C6, D5
|
||||
set coords [to_grid 5 11]
|
||||
lappend coords {*}[to_grid 5.65 11]
|
||||
lappend coords {*}[to_grid 5.85 11.2]
|
||||
lappend coords {*}[to_grid 6 11.2]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D5 vssio/vssa/vssd : Connects to D6, C6, C5
|
||||
set coords [to_grid 7 11]
|
||||
lappend coords {*}[to_grid 6.35 11]
|
||||
lappend coords {*}[to_grid 6.15 10.8]
|
||||
lappend coords {*}[to_grid 6 10.8]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E5 mprj_io[7]/irq
|
||||
set coords [list $rightpadx [lindex $rightpads 10]]
|
||||
lappend coords {*}[to_grid 12.4 10.2]
|
||||
lappend coords {*}[to_grid 9.8 10.2]
|
||||
lappend coords {*}[to_grid 9 11]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F5 mprj_io[8]/flash2 csb
|
||||
set coords [list $rightpadx [lindex $rightpads 11]]
|
||||
lappend coords {*}[to_grid 12.3 11]
|
||||
lappend coords {*}[to_grid 11 11]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A4 mprj_io[27]
|
||||
set coords [list $leftpadx [lindex $leftpads 14]]
|
||||
lappend coords {*}[to_grid -0.1 13]
|
||||
lappend coords {*}[to_grid 1 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B4 mprj_io[26]
|
||||
set coords [list $leftpadx [lindex $leftpads 15]]
|
||||
lappend coords {*}[to_grid -0.2 14]
|
||||
lappend coords {*}[to_grid 2 14]
|
||||
lappend coords {*}[to_grid 3 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C4 vddio
|
||||
set coords [list $leftpadx [lindex $leftpads 1]]
|
||||
lappend coords {*}[to_grid -0.8 2]
|
||||
lappend coords {*}[to_grid 1.8 2]
|
||||
lappend coords {*}[to_grid 2 2.2]
|
||||
lappend coords {*}[to_grid 3.3 2.2]
|
||||
lappend coords {*}[to_grid 3.8 2.7]
|
||||
lappend coords {*}[to_grid 3.8 3.7]
|
||||
lappend coords {*}[to_grid 4.3 4.2]
|
||||
lappend coords {*}[to_grid 5.3 4.2]
|
||||
lappend coords {*}[to_grid 5.8 4.7]
|
||||
lappend coords {*}[to_grid 5.8 7.4]
|
||||
lappend coords {*}[to_grid 5.2 8]
|
||||
lappend coords {*}[to_grid 4.7 8]
|
||||
lappend coords {*}[to_grid 4 8.7]
|
||||
lappend coords {*}[to_grid 4 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C4 vddio is also:
|
||||
set coords [list $leftpadx [lindex $leftpads 18]]
|
||||
lappend coords {*}[to_grid 0.1 16.2]
|
||||
lappend coords {*}[to_grid 1.6 16.2]
|
||||
lappend coords {*}[to_grid 2 15.8]
|
||||
lappend coords {*}[to_grid 3.4 15.8]
|
||||
lappend coords {*}[to_grid 4 15.2]
|
||||
lappend coords {*}[to_grid 4 13]
|
||||
lappend coords {*}[to_grid 5 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D4 vdda1
|
||||
set coords [list $rightpadx [lindex $rightpads 9]]
|
||||
lappend coords {*}[to_grid 12.8 9.8]
|
||||
lappend coords {*}[to_grid 9.7 9.8]
|
||||
lappend coords {*}[to_grid 9.5 10]
|
||||
lappend coords {*}[to_grid 8.8 10]
|
||||
lappend coords {*}[to_grid 8.2 10.6]
|
||||
lappend coords {*}[to_grid 8.2 11.8]
|
||||
lappend coords {*}[to_grid 7 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D4 vdda1 is also:
|
||||
set coords [list $rightpadx [lindex $rightpads 16]]
|
||||
lappend coords {*}[to_grid 12.6 15.8]
|
||||
lappend coords {*}[to_grid 8.4 15.8]
|
||||
lappend coords {*}[to_grid 8 15.4]
|
||||
lappend coords {*}[to_grid 8 12.4]
|
||||
lappend coords {*}[to_grid 7.8 12.2]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E4 mprj_io[9]/flash2 sck
|
||||
set coords [list $rightpadx [lindex $rightpads 12]]
|
||||
lappend coords {*}[to_grid 12.4 12]
|
||||
lappend coords {*}[to_grid 10 12]
|
||||
lappend coords {*}[to_grid 9 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F4 mprj_io[10]/flash2 io0
|
||||
set coords [list $rightpadx [lindex $rightpads 13]]
|
||||
lappend coords {*}[to_grid 12.5 13]
|
||||
lappend coords {*}[to_grid 11 13]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A3 mprj_io[25]
|
||||
set coords [list $leftpadx [lindex $leftpads 16]]
|
||||
lappend coords {*}[to_grid -0.4 15]
|
||||
lappend coords {*}[to_grid 1 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B3 vssa2
|
||||
set coords [list $leftpadx [lindex $leftpads 17]]
|
||||
lappend coords {*}[to_grid -0.4 15.8]
|
||||
lappend coords {*}[to_grid 0 15.8]
|
||||
lappend coords {*}[to_grid 1.3 15.8]
|
||||
lappend coords {*}[to_grid 2.2 15]
|
||||
lappend coords {*}[to_grid 3 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C3 mprj_io[24]
|
||||
set coords [list $leftpadx [lindex $leftpads 20]]
|
||||
lappend coords {*}[to_grid 0 18]
|
||||
lappend coords {*}[to_grid 1.5 18]
|
||||
lappend coords {*}[to_grid 2 17.5]
|
||||
lappend coords {*}[to_grid 2 16.5]
|
||||
lappend coords {*}[to_grid 2.3 16.2]
|
||||
lappend coords {*}[to_grid 3.8 16.2]
|
||||
lappend coords {*}[to_grid 5 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D3 mprj_io[13]
|
||||
set coords [list $rightpadx [lindex $rightpads 17]]
|
||||
lappend coords {*}[to_grid 12 16.2]
|
||||
lappend coords {*}[to_grid 8.2 16.2]
|
||||
lappend coords {*}[to_grid 7 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E3 mprj_io[11]/flash2 io1
|
||||
set coords [list $rightpadx [lindex $rightpads 14]]
|
||||
lappend coords {*}[to_grid 12.6 14]
|
||||
lappend coords {*}[to_grid 10 14]
|
||||
lappend coords {*}[to_grid 9 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F3 mprj_io[12]
|
||||
set coords [list $rightpadx [lindex $rightpads 15]]
|
||||
lappend coords {*}[to_grid 12.7 15]
|
||||
lappend coords {*}[to_grid 11 15]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A2 vccd2
|
||||
set coords [list $leftpadx [lindex $leftpads 19]]
|
||||
lappend coords {*}[to_grid -0.4 17.5]
|
||||
lappend coords {*}[to_grid 0.5 17.5]
|
||||
lappend coords {*}[to_grid 1 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B2 mprj_io[22]
|
||||
set coords [list [lindex $toppads 1] $toppady]
|
||||
lappend coords {*}[to_grid 2 19.7]
|
||||
lappend coords {*}[to_grid 2 18]
|
||||
lappend coords {*}[to_grid 3 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C2 mprj_io[20]
|
||||
set coords [list [lindex $toppads 3] $toppady]
|
||||
lappend coords {*}[to_grid 4 19.7]
|
||||
lappend coords {*}[to_grid 4 18]
|
||||
lappend coords {*}[to_grid 5 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D2 mprj_io[17]
|
||||
set coords [list [lindex $toppads 7] $toppady]
|
||||
lappend coords {*}[to_grid 8 19.7]
|
||||
lappend coords {*}[to_grid 8 18]
|
||||
lappend coords {*}[to_grid 7 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E2 mprj_io[14]
|
||||
set coords [list $rightpadx [lindex $rightpads 19]]
|
||||
lappend coords {*}[to_grid 12.6 18.5]
|
||||
lappend coords {*}[to_grid 12 18.5]
|
||||
lappend coords {*}[to_grid 11.5 18]
|
||||
lappend coords {*}[to_grid 10 18]
|
||||
lappend coords {*}[to_grid 9 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F2 vccd1
|
||||
set coords [list $rightpadx [lindex $rightpads 18]]
|
||||
lappend coords {*}[to_grid 12.5 17.5]
|
||||
lappend coords {*}[to_grid 11.5 17.5]
|
||||
lappend coords {*}[to_grid 11 17]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# A1 mprj_io[23]
|
||||
set coords [list [lindex $toppads 0] $toppady]
|
||||
lappend coords {*}[to_grid 1 19.7]
|
||||
lappend coords {*}[to_grid 1 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# B1 mprj_io[21]
|
||||
set coords [list [lindex $toppads 2] $toppady]
|
||||
lappend coords {*}[to_grid 3 19.7]
|
||||
lappend coords {*}[to_grid 3 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# C1 mprj_io[19]
|
||||
set coords [list [lindex $toppads 4] $toppady]
|
||||
lappend coords {*}[to_grid 5 19.7]
|
||||
lappend coords {*}[to_grid 5 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# D1 mrpj_io[18]
|
||||
set coords [list [lindex $toppads 6] $toppady]
|
||||
lappend coords {*}[to_grid 7 19.7]
|
||||
lappend coords {*}[to_grid 7 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# E1 mprj_io[16]
|
||||
set coords [list [lindex $toppads 8] $toppady]
|
||||
lappend coords {*}[to_grid 9.5 20]
|
||||
lappend coords {*}[to_grid 9.5 19.5]
|
||||
lappend coords {*}[to_grid 9 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
||||
# F1 mprj_io[15]
|
||||
set coords [list [lindex $toppads 10] $toppady]
|
||||
lappend coords {*}[to_grid 11 19.7]
|
||||
lappend coords {*}[to_grid 11 19]
|
||||
draw_pad_route $coords $wire_width
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue