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