This commit is contained in:
manarabdelaty 2021-12-02 21:20:59 +02:00
commit 85ad4b0e0f
18 changed files with 2686 additions and 327 deletions

View File

@ -82,7 +82,7 @@ SPECIAL_VOLTAGE_LIBRARY ?= sky130_fd_sc_hvl
IO_LIBRARY ?= sky130_fd_io
PRIMITIVES_LIBRARY ?= sky130_fd_pr
SKYWATER_COMMIT ?= c094b6e83a4f9298e47f696ec5a7fd53535ec5eb
OPEN_PDKS_COMMIT ?= 14db32aa8ba330e88632ff3ad2ff52f4f4dae1ad
OPEN_PDKS_COMMIT ?= 89f6ff4d9359a1ef9717b839e3e59dfdf33aaea6
INSTALL_SRAM ?= no # = yes to enable
.DEFAULT_GOAL := ship
@ -132,7 +132,8 @@ __ship:
quit -noprompt;" > $(UPRJ_ROOT)/mag/mag2gds_caravel.tcl
### Runs from CARAVEL_ROOT
@mkdir -p ./signoff/build
@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ${PDK_ROOT}/sky130A/libs.tech/magic/sky130A.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravel.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_ship.out
#@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ${PDK_ROOT}/sky130A/libs.tech/magic/sky130A.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravel.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_ship.out
@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ./.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravel.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_ship.out
### @rm $(UPRJ_ROOT)/mag/mag2gds_caravel.tcl
truck: check-env uncompress uncompress-caravel
@ -163,9 +164,9 @@ __truck:
addpath hexdigits; \
addpath $(CARAVEL_ROOT)/mag; \
addpath $(UPRJ_ROOT)/mag; \
load user_project_wrapper; \
load user_analog_project_wrapper; \
property LEFview true; \
property GDS_FILE $(UPRJ_ROOT)/gds/user_project_wrapper.gds; \
property GDS_FILE $(UPRJ_ROOT)/gds/user_analog_project_wrapper.gds; \
property GDS_START 0; \
load mgmt_core_wrapper; \
property LEFview true; \
@ -183,7 +184,8 @@ __truck:
quit -noprompt;" > $(UPRJ_ROOT)/mag/mag2gds_caravan.tcl
### Runs from CARAVEL_ROOT
@mkdir -p ./signoff/build
@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ${PDK_ROOT}/sky130A/libs.tech/magic/sky130A.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravan.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_truck.out
#@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ${PDK_ROOT}/sky130A/libs.tech/magic/sky130A.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravan.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_truck.out
@cd $(CARAVEL_ROOT)/mag && PDKPATH=${PDK_ROOT}/sky130A MAGTYPE=mag magic -noc -dnull -rcfile ./.magicrc $(UPRJ_ROOT)/mag/mag2gds_caravan.tcl 2>&1 | tee $(UPRJ_ROOT)/signoff/build/make_truck.out
### @rm $(UPRJ_ROOT)/mag/mag2gds_caravan.tcl
.PHONY: clean

BIN
gds/advSeal_6um_gen.gds.gz Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

97
mag/.magicrc Executable file
View File

@ -0,0 +1,97 @@
# 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
puts stdout "Sourcing design .magicrc for technology sky130A ..."
# Put grid on 0.005 pitch. This is important, as some commands don't
# rescale the grid automatically (such as lef read?).
set scalefac [tech lambda]
if {[lindex $scalefac 1] < 2} {
scalegrid 1 2
}
drc off
drc euclidean on
# Allow override of PDK path from environment variable PDKPATH
if {[catch {set PDKPATH $env(PDKPATH)}]} {
set PDKPATH "$::env(PDK_ROOT)/sky130A"
}
# loading technology
tech load $PDKPATH/libs.tech/magic/sky130A.tech
# load device generator
source $PDKPATH/libs.tech/magic/sky130A.tcl
# load bind keys (optional)
# source $PDKPATH/libs.tech/magic/sky130A-BindKeys
# set units to lambda grid
snap lambda
# set sky130 standard power, ground, and substrate names
set VDD VPWR
set GND VGND
set SUB VSUBS
# Allow override of type of magic library views used, "mag" or "maglef",
# from environment variable MAGTYPE
if {[catch {set MAGTYPE $env(MAGTYPE)}]} {
set MAGTYPE maglef
}
path search [concat "../$MAGTYPE" [path search]]
# add path to reference cells
if {[file isdir ${PDKPATH}/libs.ref/${MAGTYPE}]} {
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_pr
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_io
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hd
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hdll
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hs
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_hvl
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_lp
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_ls
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_fd_sc_ms
addpath ${PDKPATH}/libs.ref/${MAGTYPE}/sky130_osu_sc
addpath ${PDKPATH}/libs.ref/mag/sky130_ml_xx_hd
} else {
addpath ${PDKPATH}/libs.ref/sky130_fd_pr/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_io/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hd/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hdll/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hs/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_hvl/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_lp/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_ls/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_fd_sc_ms/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_osu_sc/${MAGTYPE}
addpath ${PDKPATH}/libs.ref/sky130_ml_xx_hd/mag
}
addpath hexdigits
addpath ../subcells/simple_por/mag
# add path to GDS cells
# add path to IP from catalog. This procedure defined in the PDK script.
catch {magic::query_mylib_ip}
# add path to local IP from user design space. Defined in the PDK script.
catch {magic::query_my_projects}

25
mag/advSeal_6um_gen.mag Executable file
View File

@ -0,0 +1,25 @@
magic
tech sky130A
timestamp 1584566829
use seal_ring_corner_abstract seal_ring_corner_abstract_0
timestamp 1584566221
transform 1 0 0 0 1 0
box 0 0 180000 260000
use seal_ring_corner_abstract seal_ring_corner_abstract_3
timestamp 1584566221
transform -1 0 360000 0 1 0
box 0 0 180000 260000
use seal_ring_corner_abstract seal_ring_corner_abstract_1
timestamp 1584566221
transform 1 0 0 0 -1 520000
box 0 0 180000 260000
use seal_ring_corner_abstract seal_ring_corner_abstract_2
timestamp 1584566221
transform -1 0 360000 0 -1 520000
box 0 0 180000 260000
<< properties >>
string LEFview no_prefix
string GDS_FILE ../gds/advSeal_6um_gen.gds
string GDS_START 0
string FIXED_BBOX 0 0 360000 520000
<< end >>

View File

@ -0,0 +1,35 @@
magic
tech sky130A
timestamp 1605459841
<< psubdiff >>
rect 145 1334 199 260000
rect 299 1334 355 260000
rect 145 1197 355 1334
rect 145 1037 531 1197
rect 145 998 652 1037
rect 253 900 652 998
rect 355 797 792 900
rect 453 795 792 797
rect 453 690 900 795
rect 565 652 900 690
rect 565 582 1049 652
rect 663 523 1049 582
rect 663 480 1197 523
rect 775 366 1197 480
rect 862 355 1197 366
rect 862 299 180000 355
rect 862 270 1511 299
rect 985 199 1511 270
rect 985 145 180000 199
<< psubdiffcont >>
rect 199 1334 299 260000
rect 1511 199 180000 299
<< locali >>
rect 199 1214 299 1334
rect 100 100 500 500
rect 1437 199 1511 299
<< metal1 >>
rect 275 325 325 420
rect 180 275 420 325
rect 275 180 325 275
<< end >>

616
scripts/check_density.py Executable file
View File

@ -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)

255
scripts/compositor.py Executable file
View File

@ -0,0 +1,255 @@
#!/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'
# pdk_root = os.getenv("PDK_ROOT")
# rcfile = pdk_root + '/sky130A/libs.tech/magic/sky130A.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)

121
scripts/count_lvs.py Executable file
View File

@ -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))

126
scripts/create-caravel-diagram.py Executable file
View File

@ -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))

View File

@ -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)

418
scripts/generate_fill.py Executable file
View File

@ -0,0 +1,418 @@
#!/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'
# pdk_root = os.getenv("PDK_ROOT")
# rcfile = pdk_root + '/sky130A/libs.tech/magic/sky130A.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)

268
scripts/generate_fill_orig.py Executable file
View File

@ -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)

702
scripts/make_bump_bonds.tcl Executable file
View File

@ -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

View File

@ -306,14 +306,20 @@ if __name__ == '__main__':
# Keep a copy of the original
if not os.path.isfile(gdsbak):
if file_zipped:
os.rename(gdsfilegz, gdsbakgz)
if os.path.isfile(gdsfilegz):
os.rename(gdsfilegz, gdsbakgz)
else:
os.rename(gdsfile, gdsbak)
subprocess.run(['gzip', gdsbak, '-n', '--best'],
stdout = subprocess.DEVNULL,
stderr = subprocess.DEVNULL)
else:
os.rename(gdsfile, gdsbak)
with open(gdsfile, 'wb') as ofile:
ofile.write(gdsdata)
if file_zipped:
subprocess.run(['gzip', gdsdata, '-n', '--best'],
if file_zipped:
subprocess.run(['gzip', gdsfile, '-n', '--best'],
stdout = subprocess.DEVNULL,
stderr = subprocess.DEVNULL)
@ -354,11 +360,17 @@ if __name__ == '__main__':
maglines = ifile.read().splitlines()
outlines = []
digit = 0
wasseen = {}
for line in maglines:
if 'alphaX_' in line:
dchar = user_id_value[digit].upper()
dchar = user_id_value[7 - digit].upper()
oline = re.sub('alpha_[0-9A-F]', 'alpha_' + dchar, line)
# Add path reference if cell was not previously found in the file
if dchar not in wasseen:
if 'hexdigits' not in oline:
oline += ' hexdigits'
outlines.append(oline)
wasseen[dchar] = True
digit += 1
else:
outlines.append(line)