summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--README23
-rw-r--r--VERSION2
-rwxr-xr-xconfigure366
-rw-r--r--configure.in117
-rw-r--r--debian/changelog17
-rw-r--r--debian/compat1
-rw-r--r--debian/control10
-rw-r--r--debian/copyright6
-rw-r--r--debian/patches/0005-Remove-references-to-excluded-files.patch16
-rw-r--r--debian/patches/0006-Fix-error-when-calling-opensta.sh.patch21
-rw-r--r--debian/patches/01_fix_install_dir.patch23
-rw-r--r--debian/patches/fix_tcsh_path.patch139
-rw-r--r--debian/patches/series1
-rw-r--r--debian/patches/yosys_version_number_minor.patch14
-rw-r--r--debian/qflow.links1
-rwxr-xr-xdebian/rules2
-rw-r--r--debian/watch2
-rw-r--r--scripts/Makefile.in48
-rwxr-xr-xscripts/addspacers.tcl.in115
-rwxr-xr-xscripts/annotate.tcl.in215
-rw-r--r--scripts/arrangepins.tcl.in28
-rwxr-xr-xscripts/checkdirs.sh.in31
-rwxr-xr-xscripts/cleanup.sh89
-rwxr-xr-xscripts/decongest.tcl.in455
-rwxr-xr-xscripts/getantennacell.tcl.in39
-rwxr-xr-xscripts/getfillcell.tcl.in20
-rwxr-xr-xscripts/graywolf.sh (renamed from scripts/placement.sh)297
-rwxr-xr-xscripts/magic_db.sh (renamed from scripts/migrate.sh)25
-rwxr-xr-xscripts/magic_drc.sh (renamed from scripts/drc.sh)22
-rwxr-xr-xscripts/magic_gds.sh (renamed from scripts/gdsii.sh)24
-rwxr-xr-xscripts/magic_view.sh (renamed from scripts/display.sh)6
-rwxr-xr-xscripts/netgen_lvs.sh (renamed from scripts/lvs.sh)30
-rwxr-xr-xscripts/opensta.sh47
-rwxr-xr-xscripts/opentimer.sh14
-rwxr-xr-xscripts/pinmanager.py.in264
-rwxr-xr-xscripts/place2def.tcl.in87
-rwxr-xr-xscripts/place2def2.tcl.in3
-rwxr-xr-xscripts/preproc.py.in452
-rw-r--r--scripts/qflow.sh.in353
-rwxr-xr-xscripts/qflow_manager.py.in749
-rwxr-xr-xscripts/qrouter.sh (renamed from scripts/router.sh)290
-rwxr-xr-xscripts/replace.sh747
-rwxr-xr-xscripts/spi2xspice.py.in32
-rwxr-xr-xscripts/textreport.py.in2
-rwxr-xr-xscripts/vesta.sh16
-rwxr-xr-xscripts/yosys.sh (renamed from scripts/synthesize.sh)341
-rw-r--r--src/DEF2Verilog.c482
-rw-r--r--src/Makefile.in87
-rw-r--r--src/addspacers.c2026
-rw-r--r--src/blif2BSpice.c4
-rw-r--r--src/blif2Verilog.c38
-rw-r--r--src/blifFanout.c86
-rw-r--r--src/hash.c134
-rw-r--r--src/hash.h51
-rw-r--r--src/lef.h239
-rw-r--r--src/rc2dly.c71
-rw-r--r--src/readdef.c2091
-rw-r--r--src/readdef.h31
-rw-r--r--src/readlef.c3436
-rw-r--r--src/readlef.h220
-rw-r--r--src/readliberty.c91
-rw-r--r--src/readliberty.h2
-rw-r--r--src/readverilog.c1989
-rw-r--r--src/readverilog.h118
-rw-r--r--src/spice2delay.c28
-rw-r--r--src/vesta.c593
-rw-r--r--src/vlog2Cel.c571
-rw-r--r--src/vlog2Def.c480
-rw-r--r--src/vlog2Spice.c636
-rw-r--r--src/vlog2Verilog.c1018
-rw-r--r--src/vlogFanout.c1735
-rw-r--r--tech/Makefile.in2
-rw-r--r--tech/osu035_redm4/Makefile.in42
l---------tech/osu035_redm4/osu035.prm1
l---------tech/osu035_redm4/osu035_redm4.magicrc.in1
l---------tech/osu035_redm4/osu035_redm4.par1
-rw-r--r--tech/osu035_redm4/osu035_redm4.sh77
l---------tech/osu035_redm4/osu035_redm4_setup.tcl1
-rw-r--r--tech/osu035_redm4/osu035_redm4_stdcells.lef3179
l---------tech/osu035_redm4/osu035_stdcells.gds21
l---------tech/osu035_redm4/osu035_stdcells.lib1
l---------tech/osu035_redm4/osu035_stdcells.sp1
l---------tech/osu035_redm4/osu035_stdcells.v1
84 files changed, 23293 insertions, 1879 deletions
diff --git a/.gitignore b/.gitignore
index 97c5ba9..237f04a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,9 @@ src/Makefile
tech/Makefile
tech/osu018/Makefile
tech/osu035/Makefile
+tech/osu035_redm4/Makefile
tech/osu050/Makefile
+tech/gscl45nm/Makefile
config.status
config.cache
config.log
@@ -31,7 +33,8 @@ src/spice2delay
src/vesta
tech/osu018/osu018.magicrc
tech/osu035/osu035.magicrc
+tech/osu035_redm4/osu035_redm4.magicrc
tech/osu050/osu050.magicrc
+tech/gscl45nm/gscl45nm.magicrc
*~
UPDATE_ME
-VERSION
diff --git a/README b/README
index fbbdcc6..0c8dedb 100644
--- a/README
+++ b/README
@@ -1,18 +1,29 @@
-qflow v.1.3
+qflow v.1.4
---------------------------------------------------------------
Tim Edwards
Open Circuit Design
-v1.0 April 2013
-v1.1 May 2015
-v1.2 May 2018
-v1.3 Dec 2018
+v1.0 April 2013
+v1.1 May 2015
+v1.2 May 2018
+v1.3 December 2018
+v1.4 December 2018
---------------------------------------------------------------
-GPL Copyright (c) 2018
+GPL Copyright (c) 2019
---------------------------------------------------------------
Default technology uses OSU open source digital cell libraries
See http://vlsiarch.ecen.okstate.edu/flows/
---------------------------------------------------------------
+NOTE: If obtaining sources through git, then first do:
+
+ git checkout qflow-1.4
+
+ for the development version, or
+
+ git checkout qflow-1.3
+
+ for the stable version.
+
To compile and install:
./configure
diff --git a/VERSION b/VERSION
index 90a7f60..a350f43 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.12
+1.4.62
diff --git a/configure b/configure
index 5b47f60..e49b8dc 100755
--- a/configure
+++ b/configure
@@ -586,6 +586,9 @@ ac_unique_file="VERSION"
ac_unique_file="Makefile.in"
ac_subst_vars='LTLIBOBJS
LIBOBJS
+HAVE_QROUTER
+HAVE_REPLACE
+HAVE_GRAYWOLF
HAVE_OPENSTA
HAVE_OPENTIMER
HAVE_NETGEN
@@ -595,6 +598,9 @@ STDLIBS
LD
QFLOW_LIB_DIR
QFLOW_BIN_DIR
+QFLOW_NTUPLACE4_PATH
+QFLOW_NTUPLACE3_PATH
+QFLOW_REPLACE_PATH
QFLOW_OPENSTA_PATH
QFLOW_OPENTIMER_PATH
QFLOW_YOSYS_PATH
@@ -681,6 +687,7 @@ with_graywolf
with_yosys
with_opentimer
with_opensta
+with_replace
with_libdir
with_bindir
'
@@ -1316,6 +1323,7 @@ Optional Packages:
--with-yosys=DIR path to yosys
--with-opentimer=DIR path to ot-shell
--with-opensta=DIR path to sta
+ --with-replace=DIR path to RePlAce
--with-libdir=DIR path to qflow runtime files
--with-bindir=DIR path to qflow launch script
@@ -4470,6 +4478,7 @@ _ACEOF
+
# Check whether --with-opensta was given.
if test "${with_opensta+set}" = set; then :
withval=$with_opensta;
@@ -4571,6 +4580,278 @@ cat >>confdefs.h <<_ACEOF
_ACEOF
+
+
+# Check whether --with-replace was given.
+if test "${with_replace+set}" = set; then :
+ withval=$with_replace;
+ if test -d "$withval"; then
+ QFLOW_REPLACE_DIR=$withval
+ elif test -f "$withval"; then
+ # Path includes "/RePlAce"; remove it
+ QFLOW_REPLACE_DIR=${withval%/RePlAce}
+ else
+ QFLOW_REPLACE_DIR=${PATH}
+ fi
+ # Extract the first word of "RePlAce", so it can be a program name with args.
+set dummy RePlAce; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_REPLACE_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_REPLACE_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_REPLACE_PATH="$QFLOW_REPLACE_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $QFLOW_REPLACE_DIR
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_REPLACE_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_REPLACE_PATH=$ac_cv_path_QFLOW_REPLACE_PATH
+if test -n "$QFLOW_REPLACE_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_REPLACE_PATH" >&5
+$as_echo "$QFLOW_REPLACE_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "ntuplace3", so it can be a program name with args.
+set dummy ntuplace3; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_NTUPLACE3_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_NTUPLACE3_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_NTUPLACE3_PATH="$QFLOW_NTUPLACE3_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${QFLOW_REPLACE_DIR}/../ntuplace
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_NTUPLACE3_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_NTUPLACE3_PATH=$ac_cv_path_QFLOW_NTUPLACE3_PATH
+if test -n "$QFLOW_NTUPLACE3_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_NTUPLACE3_PATH" >&5
+$as_echo "$QFLOW_NTUPLACE3_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "ntuplace4h", so it can be a program name with args.
+set dummy ntuplace4h; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_NTUPLACE4_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_NTUPLACE4_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_NTUPLACE4_PATH="$QFLOW_NTUPLACE4_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in ${QFLOW_REPLACE_DIR}/../ntuplace
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_NTUPLACE4_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_NTUPLACE4_PATH=$ac_cv_path_QFLOW_NTUPLACE4_PATH
+if test -n "$QFLOW_NTUPLACE4_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_NTUPLACE4_PATH" >&5
+$as_echo "$QFLOW_NTUPLACE4_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+else
+
+ # Extract the first word of "RePlAce", so it can be a program name with args.
+set dummy RePlAce; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_REPLACE_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_REPLACE_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_REPLACE_PATH="$QFLOW_REPLACE_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_REPLACE_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_REPLACE_PATH=$ac_cv_path_QFLOW_REPLACE_PATH
+if test -n "$QFLOW_REPLACE_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_REPLACE_PATH" >&5
+$as_echo "$QFLOW_REPLACE_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "ntuplace3", so it can be a program name with args.
+set dummy ntuplace3; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_NTUPLACE3_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_NTUPLACE3_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_NTUPLACE3_PATH="$QFLOW_NTUPLACE3_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_NTUPLACE3_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_NTUPLACE3_PATH=$ac_cv_path_QFLOW_NTUPLACE3_PATH
+if test -n "$QFLOW_NTUPLACE3_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_NTUPLACE3_PATH" >&5
+$as_echo "$QFLOW_NTUPLACE3_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "ntuplace4h", so it can be a program name with args.
+set dummy ntuplace4h; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_QFLOW_NTUPLACE4_PATH+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $QFLOW_NTUPLACE4_PATH in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_QFLOW_NTUPLACE4_PATH="$QFLOW_NTUPLACE4_PATH" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_QFLOW_NTUPLACE4_PATH="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+QFLOW_NTUPLACE4_PATH=$ac_cv_path_QFLOW_NTUPLACE4_PATH
+if test -n "$QFLOW_NTUPLACE4_PATH"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QFLOW_NTUPLACE4_PATH" >&5
+$as_echo "$QFLOW_NTUPLACE4_PATH" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define QFLOW_REPLACE_PATH "$QFLOW_REPLACE_PATH"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define QFLOW_NTUPLACE3_PATH "$QFLOW_NTUPLACE3_PATH"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define QFLOW_NTUPLACE4_PATH "$QFLOW_NTUPLACE4_PATH"
+_ACEOF
+
+
if test "x$prefix" = xNONE ; then
prefix=$ac_default_prefix
fi
@@ -4654,7 +4935,31 @@ fi
-ac_config_files="$ac_config_files Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu018/Makefile tech/gscl45nm/Makefile"
+if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x"; then
+ HAVE_GRAYWOLF=0
+else
+ HAVE_GRAYWOLF=1
+fi
+
+
+
+if test "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ HAVE_REPLACE=0
+else
+ HAVE_REPLACE=1
+fi
+
+
+
+if test "x${ac_cv_path_QFLOW_QROUTER_PATH}" == "x"; then
+ HAVE_QROUTER=0
+else
+ HAVE_QROUTER=1
+fi
+
+
+
+ac_config_files="$ac_config_files Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu035_redm4/Makefile tech/osu018/Makefile tech/gscl45nm/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -5368,6 +5673,7 @@ do
"tech/Makefile") CONFIG_FILES="$CONFIG_FILES tech/Makefile" ;;
"tech/osu050/Makefile") CONFIG_FILES="$CONFIG_FILES tech/osu050/Makefile" ;;
"tech/osu035/Makefile") CONFIG_FILES="$CONFIG_FILES tech/osu035/Makefile" ;;
+ "tech/osu035_redm4/Makefile") CONFIG_FILES="$CONFIG_FILES tech/osu035_redm4/Makefile" ;;
"tech/osu018/Makefile") CONFIG_FILES="$CONFIG_FILES tech/osu018/Makefile" ;;
"tech/gscl45nm/Makefile") CONFIG_FILES="$CONFIG_FILES tech/gscl45nm/Makefile" ;;
@@ -5830,57 +6136,93 @@ echo "Configuration results:"
echo "----------------------------------------------------"
echo ""
+FATAL=0
+WARN=0
if test "x${ac_cv_path_QFLOW_YOSYS_PATH}" == "x"; then
+ FATAL=1
echo "ERROR: yosys not found. Use --with-yosys=<DIR>"
else
echo "Using yosys verilog synthesis tool at: ${ac_cv_path_QFLOW_YOSYS_PATH}"
fi
+# Make sure one placement tool exists
+if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x" -a "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ FATAL=1
+ echo "ERROR: No placement tool found. Need either graywolf or RePlAce."
+fi
+
if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x"; then
- echo "ERROR: GrayWolf not found. Use --with-graywolf=<DIR>"
+ echo "GrayWolf not found. Use --with-graywolf=<DIR> (optional)"
else
echo "Using graywolf placement tool at: ${ac_cv_path_QFLOW_GRAYWOLF_PATH}"
fi
+if test "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ echo "RePlAce not found. Use --with-replace=<DIR> (optional)"
+else
+ echo "Using RePlAce placement tool at: ${ac_cv_path_QFLOW_REPLACE_PATH}"
+fi
+
if test "x${ac_cv_path_QFLOW_QROUTER_PATH}" == "x"; then
echo "ERROR: qrouter not found. Use --with-qrouter=<DIR>"
+ FATAL=1
else
echo "Using qrouter detail route tool at: ${ac_cv_path_QFLOW_QROUTER_PATH}"
fi
if test "x${ac_cv_path_QFLOW_MAGIC_PATH}" == "x"; then
+ WARN=1
echo "WARNING: Magic 8.X layout tool not found. Use --with-magic=<DIR>"
else
echo "Using Magic layout tool at: ${ac_cv_path_QFLOW_MAGIC_PATH}"
fi
if test "x${ac_cv_path_QFLOW_NETGEN_PATH}" == "x"; then
+ WARN=1
echo "WARNING: Netgen LVS tool not found. Use --with-netgen=<DIR>"
else
echo "Using Netgen LVS tool at: ${ac_cv_path_QFLOW_NETGEN_PATH}"
fi
+echo "Using Vesta STA tool (internal)"
+
if test "x${ac_cv_path_QFLOW_OPENTIMER_PATH}" == "x"; then
-# echo "INFO: OpenTimer not found. Use --with-opentimer=<DIR>"
- if test "x${ac_cv_path_QFLOW_OPENSTA_PATH}" == "x"; then
- echo "Using Vesta STA tool (internal)"
- fi
+ echo "OpenTimer not found. Use --with-opentimer=<DIR> (optional)"
else
- echo "EXPERIMENTAL: Using OpenTimer static timing analysys tool at: ${ac_cv_path_QFLOW_OPENTIMER_PATH}"
+ echo "Using OpenTimer static timing analysys tool at: ${ac_cv_path_QFLOW_OPENTIMER_PATH}"
fi
if test "x${ac_cv_path_QFLOW_OPENSTA_PATH}" == "x"; then
-# echo "INFO: OpenSTA not found. Use --with-opensta=<DIR>"
- if test "x${ac_cv_path_QFLOW_OPENTIMER_PATH}" == "x"; then
- echo "Using Vesta STA tool (internal)"
- fi
+ echo "OpenSTA not found. Use --with-opensta=<DIR> (optional)"
else
- echo "EXPERIMENTAL: Using OpenSTA static timing analysys tool at: ${ac_cv_path_QFLOW_OPENSTA_PATH}"
+ echo "Using OpenSTA static timing analysys tool at: ${ac_cv_path_QFLOW_OPENSTA_PATH}"
fi
if test "x${HAVE_PYTHON3}" == "x"; then
+ WARN=1
echo "No python3 on system, will not install qflow GUI or spi2xspice.py"
fi
echo "----------------------------------------------------"
+if test x$WARN = x1; then
+ echo "Some tools needed for a complete flow were not found. This will result in"
+ echo "an incomplete synthesis flow."
+ echo ""
+fi
+
+if test x$FATAL = x1; then
+ echo "One or more mandatory tools needed for a basic synthesis flow were not found."
+ echo "This will result in an unusable synthesis flow. Please check the configure"
+ echo "options with \"./configure --help\" and ensure that all tools are available"
+ echo "to qflow for a complete synthesis flow."
+ echo ""
+fi
+
+if test x$WARN = x0 -a x$FATAL = x0; then
+ echo "All tools needed for complete flow have been found."
+ echo "Run \"make\" followed by \"sudo make install\" to install qflow."
+ echo ""
+fi
+
+echo "----------------------------------------------------"
diff --git a/configure.in b/configure.in
index 4083d77..3673dd5 100644
--- a/configure.in
+++ b/configure.in
@@ -159,6 +159,8 @@ AC_ARG_WITH(opentimer,
AC_PATH_PROG(QFLOW_OPENTIMER_PATH, ot-shell, , )])
AC_DEFINE_UNQUOTED(QFLOW_OPENTIMER_PATH, "$QFLOW_OPENTIMER_PATH")
+dnl Path to abk-openroad OpenSTA
+
AC_ARG_WITH(opensta,
[ --with-opensta=DIR path to sta], [
if test -d "$withval"; then
@@ -174,6 +176,34 @@ AC_ARG_WITH(opensta,
AC_PATH_PROG(QFLOW_OPENSTA_PATH, sta, , )])
AC_DEFINE_UNQUOTED(QFLOW_OPENSTA_PATH, "$QFLOW_OPENSTA_PATH")
+dnl Path to abk-openroad RePlAce and detail placement ntuplace3/ntuplace4h
+
+AC_ARG_WITH(replace,
+[ --with-replace=DIR path to RePlAce], [
+ if test -d "$withval"; then
+ QFLOW_REPLACE_DIR=$withval
+ elif test -f "$withval"; then
+ # Path includes "/RePlAce"; remove it
+ QFLOW_REPLACE_DIR=${withval%/RePlAce}
+ else
+ QFLOW_REPLACE_DIR=${PATH}
+ fi
+ AC_PATH_PROG(QFLOW_REPLACE_PATH, RePlAce, , $QFLOW_REPLACE_DIR)
+ AC_PATH_PROG(QFLOW_NTUPLACE3_PATH, ntuplace3, , ${QFLOW_REPLACE_DIR}/../ntuplace)
+ AC_PATH_PROG(QFLOW_NTUPLACE4_PATH, ntuplace4h, , ${QFLOW_REPLACE_DIR}/../ntuplace)
+], [
+ AC_PATH_PROG(QFLOW_REPLACE_PATH, RePlAce, , )
+ AC_PATH_PROG(QFLOW_NTUPLACE3_PATH, ntuplace3, , )
+ AC_PATH_PROG(QFLOW_NTUPLACE4_PATH, ntuplace4h, , )
+])
+AC_DEFINE_UNQUOTED(QFLOW_REPLACE_PATH, "$QFLOW_REPLACE_PATH")
+dnl NOTE: The following is based on the current source distribution of RePlAce and
+dnl is likely to change. QFLOW_NTUPLACE_PATH is set to the common prefix "ntuplace"
+dnl of the actual tools, currently "ntuplace3" and "ntuplace4h". Probably better to
+dnl write a custom autoconf script for this.
+AC_DEFINE_UNQUOTED(QFLOW_NTUPLACE3_PATH, "$QFLOW_NTUPLACE3_PATH")
+AC_DEFINE_UNQUOTED(QFLOW_NTUPLACE4_PATH, "$QFLOW_NTUPLACE4_PATH")
+
dnl Expand the prefix variable
if test "x$prefix" = xNONE ; then
prefix=$ac_default_prefix
@@ -197,6 +227,9 @@ AC_ARG_WITH(bindir,
dnl AC_SUBST(QFLOW_YOSYS_PATH)
dnl AC_SUBST(QFLOW_GRAYWOLF_PATH)
+dnl AC_SUBST(QFLOW_REPLACE_PATH)
+dnl AC_SUBST(QFLOW_NTUPLACE3_PATH)
+dnl AC_SUBST(QFLOW_NTUPLACE4_PATH)
dnl AC_SUBST(QFLOW_QROUTER_PATH)
dnl AC_SUBST(QFLOW_MAGIC_PATH)
dnl AC_SUBST(QFLOW_NETGEN_PATH)
@@ -254,64 +287,124 @@ fi
AC_SUBST(HAVE_OPENSTA)
-AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu018/Makefile)
+if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x"; then
+ HAVE_GRAYWOLF=0
+else
+ HAVE_GRAYWOLF=1
+fi
+
+AC_SUBST(HAVE_GRAYWOLF)
+
+if test "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ HAVE_REPLACE=0
+else
+ HAVE_REPLACE=1
+fi
+
+AC_SUBST(HAVE_REPLACE)
+
+if test "x${ac_cv_path_QFLOW_QROUTER_PATH}" == "x"; then
+ HAVE_QROUTER=0
+else
+ HAVE_QROUTER=1
+fi
+
+AC_SUBST(HAVE_QROUTER)
+
+AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu035_redm4/Makefile tech/osu018/Makefile)
echo ""
echo "Configuration results:"
echo "----------------------------------------------------"
echo ""
+FATAL=0
+WARN=0
if test "x${ac_cv_path_QFLOW_YOSYS_PATH}" == "x"; then
+ FATAL=1
echo "ERROR: yosys not found. Use --with-yosys=<DIR>"
else
echo "Using yosys verilog synthesis tool at: ${ac_cv_path_QFLOW_YOSYS_PATH}"
fi
+# Make sure one placement tool exists
+if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x" -a "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ FATAL=1
+ echo "ERROR: No placement tool found. Need either graywolf or RePlAce."
+fi
+
if test "x${ac_cv_path_QFLOW_GRAYWOLF_PATH}" == "x"; then
- echo "ERROR: GrayWolf not found. Use --with-graywolf=<DIR>"
+ echo "GrayWolf not found. Use --with-graywolf=<DIR> (optional)"
else
echo "Using graywolf placement tool at: ${ac_cv_path_QFLOW_GRAYWOLF_PATH}"
fi
+if test "x${ac_cv_path_QFLOW_REPLACE_PATH}" == "x"; then
+ echo "RePlAce not found. Use --with-replace=<DIR> (optional)"
+else
+ echo "Using RePlAce placement tool at: ${ac_cv_path_QFLOW_REPLACE_PATH}"
+fi
+
if test "x${ac_cv_path_QFLOW_QROUTER_PATH}" == "x"; then
echo "ERROR: qrouter not found. Use --with-qrouter=<DIR>"
+ FATAL=1
else
echo "Using qrouter detail route tool at: ${ac_cv_path_QFLOW_QROUTER_PATH}"
fi
if test "x${ac_cv_path_QFLOW_MAGIC_PATH}" == "x"; then
+ WARN=1
echo "WARNING: Magic 8.X layout tool not found. Use --with-magic=<DIR>"
else
echo "Using Magic layout tool at: ${ac_cv_path_QFLOW_MAGIC_PATH}"
fi
if test "x${ac_cv_path_QFLOW_NETGEN_PATH}" == "x"; then
+ WARN=1
echo "WARNING: Netgen LVS tool not found. Use --with-netgen=<DIR>"
else
echo "Using Netgen LVS tool at: ${ac_cv_path_QFLOW_NETGEN_PATH}"
fi
+echo "Using Vesta STA tool (internal)"
+
if test "x${ac_cv_path_QFLOW_OPENTIMER_PATH}" == "x"; then
-# echo "INFO: OpenTimer not found. Use --with-opentimer=<DIR>"
- if test "x${ac_cv_path_QFLOW_OPENSTA_PATH}" == "x"; then
- echo "Using Vesta STA tool (internal)"
- fi
+ echo "OpenTimer not found. Use --with-opentimer=<DIR> (optional)"
else
- echo "EXPERIMENTAL: Using OpenTimer static timing analysys tool at: ${ac_cv_path_QFLOW_OPENTIMER_PATH}"
+ echo "Using OpenTimer static timing analysys tool at: ${ac_cv_path_QFLOW_OPENTIMER_PATH}"
fi
if test "x${ac_cv_path_QFLOW_OPENSTA_PATH}" == "x"; then
-# echo "INFO: OpenSTA not found. Use --with-opensta=<DIR>"
- if test "x${ac_cv_path_QFLOW_OPENTIMER_PATH}" == "x"; then
- echo "Using Vesta STA tool (internal)"
- fi
+ echo "OpenSTA not found. Use --with-opensta=<DIR> (optional)"
else
- echo "EXPERIMENTAL: Using OpenSTA static timing analysys tool at: ${ac_cv_path_QFLOW_OPENSTA_PATH}"
+ echo "Using OpenSTA static timing analysys tool at: ${ac_cv_path_QFLOW_OPENSTA_PATH}"
fi
if test "x${HAVE_PYTHON3}" == "x"; then
+ WARN=1
echo "No python3 on system, will not install qflow GUI or spi2xspice.py"
fi
echo "----------------------------------------------------"
+if test x$WARN = x1; then
+ echo "Some tools needed for a complete flow were not found. This will result in"
+ echo "an incomplete synthesis flow."
+ echo ""
+fi
+
+if test x$FATAL = x1; then
+ echo "One or more mandatory tools needed for a basic synthesis flow were not found."
+ echo "This will result in an unusable synthesis flow. Please check the configure"
+ echo "options with \"./configure --help\" and ensure that all tools are available"
+ echo "to qflow for a complete synthesis flow."
+ echo ""
+fi
+
+if test x$WARN = x0 -a x$FATAL = x0; then
+ echo "All tools needed for complete flow have been found."
+ echo "Run \"make\" followed by \"sudo make install\" to install qflow."
+ echo ""
+fi
+
+echo "----------------------------------------------------"
diff --git a/debian/changelog b/debian/changelog
index 14b8149..37d8396 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,20 @@
+qflow (1.4.62+dfsg.1-1~exp1) experimental; urgency=low
+
+ * New upstream release
+
+ -- Ruben Undheim <ruben.undheim@gmail.com> Sat, 19 Oct 2019 21:13:08 +0200
+
+qflow (1.3.17+dfsg.1-1) unstable; urgency=medium
+
+ * Upload to sid
+ * New upstream release
+ * debian/control:
+ - New standards version 4.4.1 - no changes
+ - Use debhelper-compat build-dependency instead of debian/compat file
+ - DH level 12
+
+ -- Ruben Undheim <ruben.undheim@gmail.com> Sat, 19 Oct 2019 11:10:52 +0200
+
qflow (1.3.12+dfsg.1-1~exp1) experimental; urgency=medium
* New upstream release
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index b4de394..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-11
diff --git a/debian/control b/debian/control
index 53b0333..54bbad9 100644
--- a/debian/control
+++ b/debian/control
@@ -3,12 +3,12 @@ Maintainer: Debian Science Maintainers <debian-science-maintainers@lists.alioth.
Uploaders: Ruben Undheim <ruben.undheim@gmail.com>
Section: electronics
Priority: optional
-Build-Depends: debhelper (>= 11),
+Build-Depends: debhelper-compat (= 12),
yosys,
graywolf,
qrouter,
magic
-Standards-Version: 4.3.0
+Standards-Version: 4.4.1
Vcs-Browser: https://salsa.debian.org/science-team/qflow
Vcs-Git: https://salsa.debian.org/science-team/qflow.git
Homepage: http://opencircuitdesign.com/qflow/
@@ -27,9 +27,9 @@ Depends: ${shlibs:Depends},
netgen-lvs,
python3
Recommends: python3-tk,
- qflow-tech-osu018,
- qflow-tech-osu035,
- qflow-tech-osu050
+ qflow-tech-osu018 (= ${source:Version}),
+ qflow-tech-osu035 (= ${source:Version}),
+ qflow-tech-osu050 (= ${source:Version})
Description: Open-Source Digital Synthesis Flow
This is a complete tool chain for synthesizing digital circuits starting
from verilog source and ending in physical layout for a specific target
diff --git a/debian/copyright b/debian/copyright
index 40e06eb..78ac04f 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -7,7 +7,7 @@ Files-Excluded: tech/osu018/osu018_stdcells.gds2
tech/gscl45nm
Files: *
-Copyright: 2006-2018 Tim Edwards <tim@opencircuitdesign.com>
+Copyright: 2006-2019 Tim Edwards <tim@opencircuitdesign.com>
License: GPL
Files: src/hash.c
@@ -24,7 +24,7 @@ Copyright: 2008 Steve Beccue
2013-2015 Tim Edwards <tim@opencircuitdesign.com>
License: GPL
-Files: scripts/synthesize.sh
+Files: scripts/yosys.sh
Copyright: 2006 Steve Beccue
2006-2015 Tim Edwards <tim@opencircuitdesign.com>
License: GPL
@@ -40,7 +40,7 @@ Copyright: 2006-2017 Tim Edwards <tim@opencircuitdesign.com>
License: GPL
Files: debian/*
-Copyright: 2014-2018 Ruben Undheim <ruben.undheim@gmail.com>
+Copyright: 2014-2019 Ruben Undheim <ruben.undheim@gmail.com>
License: GPL
diff --git a/debian/patches/0005-Remove-references-to-excluded-files.patch b/debian/patches/0005-Remove-references-to-excluded-files.patch
index e9c5410..45f8cbc 100644
--- a/debian/patches/0005-Remove-references-to-excluded-files.patch
+++ b/debian/patches/0005-Remove-references-to-excluded-files.patch
@@ -11,28 +11,28 @@ Subject: Remove references to excluded files
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/configure.in b/configure.in
-index 988e851..4083d77 100644
+index d581bef..3673dd5 100644
--- a/configure.in
+++ b/configure.in
-@@ -254,7 +254,7 @@ fi
+@@ -311,7 +311,7 @@ fi
- AC_SUBST(HAVE_OPENSTA)
+ AC_SUBST(HAVE_QROUTER)
--AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu018/Makefile tech/gscl45nm/Makefile)
-+AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu018/Makefile)
+-AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu035_redm4/Makefile tech/osu018/Makefile tech/gscl45nm/Makefile)
++AC_OUTPUT(Makefile scripts/Makefile src/Makefile tech/Makefile tech/osu050/Makefile tech/osu035/Makefile tech/osu035_redm4/Makefile tech/osu018/Makefile)
echo ""
echo "Configuration results:"
diff --git a/tech/Makefile.in b/tech/Makefile.in
-index 6b0666d..73b02a6 100644
+index eb0b326..1cfbf91 100644
--- a/tech/Makefile.in
+++ b/tech/Makefile.in
@@ -11,7 +11,7 @@ INSTALL = @INSTALL@
QFLOW_LIB_DIR = /usr/share/qflow
--TECH_DIRS = osu050 osu035 osu018 gscl45nm
-+TECH_DIRS = osu050 osu035 osu018
+-TECH_DIRS = osu050 osu035 osu035_redm4 osu018 gscl45nm
++TECH_DIRS = osu050 osu035 osu035_redm4 osu018
TECHINSTALL = ${QFLOW_LIB_DIR}/tech
diff --git a/debian/patches/0006-Fix-error-when-calling-opensta.sh.patch b/debian/patches/0006-Fix-error-when-calling-opensta.sh.patch
new file mode 100644
index 0000000..826008d
--- /dev/null
+++ b/debian/patches/0006-Fix-error-when-calling-opensta.sh.patch
@@ -0,0 +1,21 @@
+From: Ruben Undheim <ruben.undheim@gmail.com>
+Date: Thu, 28 Mar 2019 22:28:30 +0100
+Subject: Fix error when calling opensta.sh
+
+---
+ scripts/opentimer.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/scripts/opentimer.sh b/scripts/opentimer.sh
+index 8902d8a..40236f6 100755
+--- a/scripts/opentimer.sh
++++ b/scripts/opentimer.sh
+@@ -143,7 +143,7 @@ endif
+
+ # Add hard macros
+
+-hardmacrolibs = ""
++set hardmacrolibs = ""
+ if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
diff --git a/debian/patches/01_fix_install_dir.patch b/debian/patches/01_fix_install_dir.patch
index eaddf57..d0f255d 100644
--- a/debian/patches/01_fix_install_dir.patch
+++ b/debian/patches/01_fix_install_dir.patch
@@ -4,14 +4,14 @@ Subject: This patch fixes the install dir
---
scripts/Makefile.in | 12 ++++++------
- src/Makefile.in | 2 +-
+ src/Makefile.in | 3 +--
tech/Makefile.in | 2 +-
tech/osu035/Makefile.in | 2 +-
tech/osu050/Makefile.in | 2 +-
- 5 files changed, 10 insertions(+), 10 deletions(-)
+ 5 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
-index 9d02a97..5be9273 100644
+index 8918b18..bdac8bf 100644
--- a/scripts/Makefile.in
+++ b/scripts/Makefile.in
@@ -12,8 +12,8 @@ INSTALL = @INSTALL@
@@ -25,7 +25,7 @@ index 9d02a97..5be9273 100644
TCLSH_PATH = @TCLSH_PATH@
ENV_PATH = @ENV_PATH@
HAVE_PYTHON3 = @HAVE_PYTHON3@
-@@ -41,7 +41,7 @@ SHELL_SCRIPTS += drc.sh lvs.sh gdsii.sh migrate.sh
+@@ -66,7 +66,7 @@ SHELL_SCRIPTS += cleanup.sh
MAIN_SCRIPT = qflow
SCRIPTINSTALL = ${QFLOW_LIB_DIR}/scripts
@@ -34,7 +34,7 @@ index 9d02a97..5be9273 100644
QFLOWEXECPATH = ${QFLOW_LIB_DIR}/bin
EXECINSTALL = ${QFLOW_BIN_DIR}
-@@ -53,9 +53,9 @@ launcher: $(MAIN_SCRIPT).in
+@@ -78,9 +78,9 @@ launcher: $(MAIN_SCRIPT).in
$(MAIN_SCRIPT).in > $(MAIN_SCRIPT)
checkdirs.sh: checkdirs.sh.in
@@ -48,20 +48,21 @@ index 9d02a97..5be9273 100644
qflow.sh: qflow.sh.in
diff --git a/src/Makefile.in b/src/Makefile.in
-index 9214646..501bdf9 100644
+index 0b15324..8d1b8ff 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
-@@ -10,7 +10,7 @@ INSTALL = @INSTALL@
+@@ -10,8 +10,7 @@ INSTALL = @INSTALL@
DEFS = @DEFS@ -DQFLOW_VERSION=\"@VERSION@\" -DQFLOW_REVISION=\"@REVISION@\"
-QFLOW_LIB_DIR = @QFLOW_LIB_DIR@
+-
+QFLOW_LIB_DIR = /usr/lib/qflow
QFLOW_GRAYWOLF_PATH = @QFLOW_GRAYWOLF_PATH@
- QFLOW_QROUTER_PATH = @QFLOW_QROUTER_PATH@
- QFLOW_ABC_PATH = @QFLOW_ABC_PATH@
+ QFLOW_REPLACE_PATH = @QFLOW_REPLACE_PATH@
+ QFLOW_NTUPLACE3_PATH = @QFLOW_NTUPLACE3_PATH@
diff --git a/tech/Makefile.in b/tech/Makefile.in
-index 2a28d94..6b0666d 100644
+index e4e6bc1..eb0b326 100644
--- a/tech/Makefile.in
+++ b/tech/Makefile.in
@@ -9,7 +9,7 @@ LIBS = @LIBS@
@@ -71,7 +72,7 @@ index 2a28d94..6b0666d 100644
-QFLOW_LIB_DIR = @QFLOW_LIB_DIR@
+QFLOW_LIB_DIR = /usr/share/qflow
- TECH_DIRS = osu050 osu035 osu018 gscl45nm
+ TECH_DIRS = osu050 osu035 osu035_redm4 osu018 gscl45nm
diff --git a/tech/osu035/Makefile.in b/tech/osu035/Makefile.in
index 60d1a75..b6f999c 100644
diff --git a/debian/patches/fix_tcsh_path.patch b/debian/patches/fix_tcsh_path.patch
index f5a1fde..99c9770 100644
--- a/debian/patches/fix_tcsh_path.patch
+++ b/debian/patches/fix_tcsh_path.patch
@@ -5,27 +5,28 @@ Subject: This patch fixes the path to the tcsh interpreter.
---
scripts/checkdirs.sh.in | 2 +-
scripts/cleanup.sh | 2 +-
- scripts/display.sh | 2 +-
- scripts/drc.sh | 2 +-
- scripts/gdsii.sh | 2 +-
- scripts/lvs.sh | 2 +-
+ scripts/graywolf.sh | 2 +-
+ scripts/magic_db.sh | 2 +-
+ scripts/magic_drc.sh | 2 +-
+ scripts/magic_gds.sh | 2 +-
+ scripts/magic_view.sh | 2 +-
scripts/makesim.sh | 2 +-
- scripts/migrate.sh | 2 +-
+ scripts/netgen_lvs.sh | 2 +-
scripts/opensta.sh | 2 +-
scripts/opentimer.sh | 2 +-
- scripts/placement.sh | 2 +-
scripts/qflow.in | 2 +-
scripts/qflow.sh.in | 8 ++++----
- scripts/router.sh | 2 +-
- scripts/synthesize.sh | 2 +-
+ scripts/qrouter.sh | 2 +-
+ scripts/replace.sh | 2 +-
scripts/vesta.sh | 2 +-
+ scripts/yosys.sh | 2 +-
tech/osu018/osu018.sh | 2 +-
tech/osu035/osu035.sh | 2 +-
tech/osu050/osu050.sh | 2 +-
- 19 files changed, 22 insertions(+), 22 deletions(-)
+ 20 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/scripts/checkdirs.sh.in b/scripts/checkdirs.sh.in
-index 3d9878b..1d222d9 100755
+index 501969f..4a44e5c 100755
--- a/scripts/checkdirs.sh.in
+++ b/scripts/checkdirs.sh.in
@@ -1,4 +1,4 @@
@@ -35,7 +36,7 @@ index 3d9878b..1d222d9 100755
#-------------------------------------------------------------------------
# checkdirs.sh
diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh
-index 99d1777..fa33102 100755
+index e869444..051b7ce 100755
--- a/scripts/cleanup.sh
+++ b/scripts/cleanup.sh
@@ -1,4 +1,4 @@
@@ -44,45 +45,55 @@ index 99d1777..fa33102 100755
#----------------------------------------------------------
# Workspace cleanup script for qflow
#----------------------------------------------------------
-diff --git a/scripts/display.sh b/scripts/display.sh
-index 5a4ea56..f3b397e 100755
---- a/scripts/display.sh
-+++ b/scripts/display.sh
+diff --git a/scripts/graywolf.sh b/scripts/graywolf.sh
+index 40dd816..25500c7 100755
+--- a/scripts/graywolf.sh
++++ b/scripts/graywolf.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
- # Qflow layout display script using magic-8.0
+ # Placement script using GrayWolf
+ # Prerequisites: Mapped verilog netlist from synthesis.
+diff --git a/scripts/magic_db.sh b/scripts/magic_db.sh
+index 0dd3c6f..3454e6a 100755
+--- a/scripts/magic_db.sh
++++ b/scripts/magic_db.sh
+@@ -1,4 +1,4 @@
+-#!/bin/tcsh -f
++#!/usr/bin/tcsh -f
+ #----------------------------------------------------------
+ # Layout and netlist migration script using magic database
#----------------------------------------------------------
-diff --git a/scripts/drc.sh b/scripts/drc.sh
-index 509e2c8..0ad344a 100755
---- a/scripts/drc.sh
-+++ b/scripts/drc.sh
+diff --git a/scripts/magic_drc.sh b/scripts/magic_drc.sh
+index 2dea6fd..04e2fd2 100755
+--- a/scripts/magic_drc.sh
++++ b/scripts/magic_drc.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
# DRC error checking script using magic
#----------------------------------------------------------
-diff --git a/scripts/gdsii.sh b/scripts/gdsii.sh
-index d6ea267..4c2ac41 100755
---- a/scripts/gdsii.sh
-+++ b/scripts/gdsii.sh
+diff --git a/scripts/magic_gds.sh b/scripts/magic_gds.sh
+index 43041f9..5a1de27 100755
+--- a/scripts/magic_gds.sh
++++ b/scripts/magic_gds.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
# GDSII output generating script using magic
#----------------------------------------------------------
-diff --git a/scripts/lvs.sh b/scripts/lvs.sh
-index 977668c..dadc591 100755
---- a/scripts/lvs.sh
-+++ b/scripts/lvs.sh
+diff --git a/scripts/magic_view.sh b/scripts/magic_view.sh
+index bb505b8..687d5ea 100755
+--- a/scripts/magic_view.sh
++++ b/scripts/magic_view.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
- # LVS comparison script using magic
+ # Qflow layout display script using magic-8.X
#----------------------------------------------------------
diff --git a/scripts/makesim.sh b/scripts/makesim.sh
index db3af36..e658596 100755
@@ -94,18 +105,18 @@ index db3af36..e658596 100755
#
# makesim.sh cellname
#
-diff --git a/scripts/migrate.sh b/scripts/migrate.sh
-index 9924a85..79b74b5 100755
---- a/scripts/migrate.sh
-+++ b/scripts/migrate.sh
+diff --git a/scripts/netgen_lvs.sh b/scripts/netgen_lvs.sh
+index 74ebd99..b1976ab 100755
+--- a/scripts/netgen_lvs.sh
++++ b/scripts/netgen_lvs.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
- # Layout and netlist migration script
+ # LVS comparison script using netgen
#----------------------------------------------------------
diff --git a/scripts/opensta.sh b/scripts/opensta.sh
-index 9b71c56..4ac20f7 100755
+index b131507..162e790 100755
--- a/scripts/opensta.sh
+++ b/scripts/opensta.sh
@@ -1,4 +1,4 @@
@@ -115,7 +126,7 @@ index 9b71c56..4ac20f7 100755
# Static timing analysis script using OpenSTA
#----------------------------------------------------------
diff --git a/scripts/opentimer.sh b/scripts/opentimer.sh
-index d1d3c52..16acad7 100755
+index 14cb0f1..8902d8a 100755
--- a/scripts/opentimer.sh
+++ b/scripts/opentimer.sh
@@ -1,4 +1,4 @@
@@ -124,16 +135,6 @@ index d1d3c52..16acad7 100755
#----------------------------------------------------------
# Static timing analysis script using OpenTimer
#----------------------------------------------------------
-diff --git a/scripts/placement.sh b/scripts/placement.sh
-index 832d833..73e3068 100755
---- a/scripts/placement.sh
-+++ b/scripts/placement.sh
-@@ -1,4 +1,4 @@
--#!/bin/tcsh -f
-+#!/usr/bin/tcsh -f
- #----------------------------------------------------------
- # Placement script using GrayWolf
- #
diff --git a/scripts/qflow.in b/scripts/qflow.in
index 2b6dfd7..3fa2076 100755
--- a/scripts/qflow.in
@@ -145,7 +146,7 @@ index 2b6dfd7..3fa2076 100755
#------------------------------------------------------------------
# qflow --- main program launcher
diff --git a/scripts/qflow.sh.in b/scripts/qflow.sh.in
-index 8bbdbfb..e6026ae 100644
+index 0df1ad7..9225263 100644
--- a/scripts/qflow.sh.in
+++ b/scripts/qflow.sh.in
@@ -1,4 +1,4 @@
@@ -154,55 +155,55 @@ index 8bbdbfb..e6026ae 100644
#
#------------------------------------------------------------------
# qflow.sh --- main program shell script
-@@ -379,7 +379,7 @@ if ( -f $varfile ) then
+@@ -390,7 +390,7 @@ if ( -f $varfile ) then
endif
endif
-echo "#\!/bin/tcsh -f" > ${varfile}
+echo "#\!/usr/bin/tcsh -f" > ${varfile}
echo "#-------------------------------------------" >> ${varfile}
- echo "# qflow variables for project ${project}" >> ${varfile}
+ echo "# qflow variables for project ${projectpath}" >> ${varfile}
echo "#-------------------------------------------" >> ${varfile}
-@@ -397,7 +397,7 @@ echo "set logdir=${projectpath}/log" >> ${varfile}
+@@ -408,7 +408,7 @@ echo "set logdir=${projectpath}/log" >> ${varfile}
echo "#-------------------------------------------" >> ${varfile}
echo "" >> ${varfile}
-echo "#\!/bin/tcsh -f" > ${execfile}
+echo "#\!/usr/bin/tcsh -f" > ${execfile}
echo "#-------------------------------------------" >> ${execfile}
- echo "# qflow exec script for project ${project}" >> ${execfile}
+ echo "# qflow exec script for project ${projectpath}" >> ${execfile}
echo "#-------------------------------------------" >> ${execfile}
-@@ -411,7 +411,7 @@ echo "" >> ${execfile}
+@@ -480,7 +480,7 @@ endif
#-----------------------------------------------------
if ( ! -f ${userfile} ) then
- echo "#\!/bin/tcsh -f" > ${userfile}
+ echo "#\!/usr/bin/tcsh -f" > ${userfile}
echo "#------------------------------------------------------------" >> ${userfile}
- echo "# project variables for project ${project}" >> ${userfile}
+ echo "# project variables for project ${projectpath}" >> ${userfile}
echo "#------------------------------------------------------------" >> ${userfile}
-diff --git a/scripts/router.sh b/scripts/router.sh
-index 851f55a..6fa5081 100755
---- a/scripts/router.sh
-+++ b/scripts/router.sh
+diff --git a/scripts/qrouter.sh b/scripts/qrouter.sh
+index d28df33..b320a1e 100755
+--- a/scripts/qrouter.sh
++++ b/scripts/qrouter.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
#----------------------------------------------------------
# Route script using qrouter
#----------------------------------------------------------
-diff --git a/scripts/synthesize.sh b/scripts/synthesize.sh
-index fe187ab..c938baa 100755
---- a/scripts/synthesize.sh
-+++ b/scripts/synthesize.sh
+diff --git a/scripts/replace.sh b/scripts/replace.sh
+index 8ed6769..6e7c47e 100755
+--- a/scripts/replace.sh
++++ b/scripts/replace.sh
@@ -1,4 +1,4 @@
-#!/bin/tcsh -f
+#!/usr/bin/tcsh -f
+ #----------------------------------------------------------
+ # Placement script using abk-openroad RePlAce
#
- # synthesize.sh:
- #-------------------------------------------------------------------------
diff --git a/scripts/vesta.sh b/scripts/vesta.sh
-index cbdc02a..5d3c467 100755
+index 388516d..15ebc89 100755
--- a/scripts/vesta.sh
+++ b/scripts/vesta.sh
@@ -1,4 +1,4 @@
@@ -211,6 +212,16 @@ index cbdc02a..5d3c467 100755
#----------------------------------------------------------
# Static timing analysis script using vesta
#----------------------------------------------------------
+diff --git a/scripts/yosys.sh b/scripts/yosys.sh
+index 29cff44..deb8f2a 100755
+--- a/scripts/yosys.sh
++++ b/scripts/yosys.sh
+@@ -1,4 +1,4 @@
+-#!/bin/tcsh -f
++#!/usr/bin/tcsh -f
+ #
+ # yosys.sh:
+ #-------------------------------------------------------------------------
diff --git a/tech/osu018/osu018.sh b/tech/osu018/osu018.sh
index 16808e8..fac486b 100644
--- a/tech/osu018/osu018.sh
diff --git a/debian/patches/series b/debian/patches/series
index 699ad5f..8547189 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -3,3 +3,4 @@ permission_technology.patch
fix_tcsh_path.patch
yosys_version_number_minor.patch
0005-Remove-references-to-excluded-files.patch
+0006-Fix-error-when-calling-opensta.sh.patch
diff --git a/debian/patches/yosys_version_number_minor.patch b/debian/patches/yosys_version_number_minor.patch
index fa3bc21..6498d94 100644
--- a/debian/patches/yosys_version_number_minor.patch
+++ b/debian/patches/yosys_version_number_minor.patch
@@ -4,18 +4,18 @@ Subject: qflow doesn't work with versions of yosys
with a "+" in the version string. This patch fixes this.
---
- scripts/synthesize.sh | 1 +
+ scripts/yosys.sh | 1 +
1 file changed, 1 insertion(+)
-diff --git a/scripts/synthesize.sh b/scripts/synthesize.sh
-index c938baa..75031e9 100755
---- a/scripts/synthesize.sh
-+++ b/scripts/synthesize.sh
-@@ -138,6 +138,7 @@ else
+diff --git a/scripts/yosys.sh b/scripts/yosys.sh
+index deb8f2a..3f9bae5 100755
+--- a/scripts/yosys.sh
++++ b/scripts/yosys.sh
+@@ -157,6 +157,7 @@ else
set subrevision = ${minortest}
endif
+set minor = `echo ${minor} | sed 's/+//'`
+
# Check if "yosys_options" specifies a script to use for yosys.
- if ( ! ${?yosys_options} ) then
diff --git a/debian/qflow.links b/debian/qflow.links
index 4c24ee1..2d3203f 100644
--- a/debian/qflow.links
+++ b/debian/qflow.links
@@ -1 +1,2 @@
/usr/bin/netgen-lvs /usr/lib/qflow/bin/netgen
+/usr/bin/qrouter /usr/lib/qflow/bin/qrouter
diff --git a/debian/rules b/debian/rules
index a46e013..ac532cc 100755
--- a/debian/rules
+++ b/debian/rules
@@ -14,6 +14,8 @@ override_dh_clean:
dh_clean
$(RM) -r scripts/Makefile src/Makefile tech/Makefile config.log config.status Makefile
$(RM) -r tech/osu018/Makefile tech/osu035/Makefile tech/osu050/Makefile
+ $(RM) -r tech/osu035_redm4/Makefile tech/osu035_redm4/osu035_redm4.magicrc
+
override_dh_auto_install:
dh_auto_install
diff --git a/debian/watch b/debian/watch
index 569e66d..a0c5d6e 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,4 +1,4 @@
version=3
opts=dversionmangle=s/([^\+]+)(?:\+dfsg.*)?/$1/,\
oversionmangle=s/(.*)/$1+dfsg.1/ \
-http://opencircuitdesign.com/qflow/archive/qflow-(1\.1\.\d\S+)\.tgz
+http://opencircuitdesign.com/qflow/archive/qflow-(1\.4\.\d\S+)\.tgz
diff --git a/scripts/Makefile.in b/scripts/Makefile.in
index 5be9273..bdac8bf 100644
--- a/scripts/Makefile.in
+++ b/scripts/Makefile.in
@@ -17,8 +17,16 @@ QFLOW_BIN_DIR = /usr/bin
TCLSH_PATH = @TCLSH_PATH@
ENV_PATH = @ENV_PATH@
HAVE_PYTHON3 = @HAVE_PYTHON3@
+
+# Flow tools (NOTE: Vesta is part of qflow, so HAVE_VESTA is implicitly true)
+HAVE_YOSYS = @HAVE_YOSYS@
HAVE_OPENTIMER = @HAVE_OPENTIMER@
HAVE_OPENSTA = @HAVE_OPENSTA@
+HAVE_GRAYWOLF = @HAVE_GRAYWOLF@
+HAVE_REPLACE = @HAVE_REPLACE@
+HAVE_QROUTER = @HAVE_QROUTER@
+HAVE_MAGIC = @HAVE_MAGIC@
+HAVE_NETGEN = @HAVE_NETGEN@
DEFAULTPARSER = @QFLOW_DEFAULT_PARSER@
@@ -30,14 +38,31 @@ TCL_SCRIPTS += powerbus.tcl getpowerground.tcl arrangepins.tcl
TCL_SCRIPTS += removeblocks.tcl
PYTHON_SCRIPTS = spi2xspice.py consoletext.py pinmanager.py
PYTHON_SCRIPTS += tksimpledialog.py helpwindow.py qflow_manager.py
-PYTHON_SCRIPTS += textreport.py tooltip.py count_lvs.py
+PYTHON_SCRIPTS += textreport.py tooltip.py count_lvs.py preproc.py
HELP_TEXT = qflow_help.txt
-SHELL_SCRIPTS = synthesize.sh placement.sh router.sh
+# Master list of synthesis flow scripts to install
+# Synthesis scripts:
+SHELL_SCRIPTS = yosys.sh
+# Placement scripts:
+SHELL_SCRIPTS += graywolf.sh replace.sh
+# Static timing analysys scripts:
SHELL_SCRIPTS += vesta.sh opentimer.sh opensta.sh
+# Router scripts:
+SHELL_SCRIPTS += qrouter.sh
+# Database migration scripts:
+SHELL_SCRIPTS += magic_db.sh
+# DRC scripts:
+SHELL_SCRIPTS += magic_drc.sh
+# LVS scripts:
+SHELL_SCRIPTS += netgen_lvs.sh
+# GDS scripts:
+SHELL_SCRIPTS += magic_gds.sh
+# Display scripts:
+SHELL_SCRIPTS += magic_view.sh
+
SHELL_SCRIPTS += qflow.sh checkdirs.sh
-SHELL_SCRIPTS += cleanup.sh display.sh
-SHELL_SCRIPTS += drc.sh lvs.sh gdsii.sh migrate.sh
+SHELL_SCRIPTS += cleanup.sh
MAIN_SCRIPT = qflow
SCRIPTINSTALL = ${QFLOW_LIB_DIR}/scripts
@@ -63,8 +88,14 @@ qflow.sh: qflow.sh.in
-e '/QFLOW_DEFAULT_PARSER/s#QFLOW_DEFAULT_PARSER#$(DEFAULTPARSER)#' \
-e '/QFLOW_REVISION/s#QFLOW_REVISION#$(REVISION)#' \
-e '/QFLOW_VERSION/s#QFLOW_VERSION#$(VERSION)#' \
+ -e '/HAVE_YOSYS/s#HAVE_YOSYS#$(HAVE_YOSYS)#' \
+ -e '/HAVE_GRAYWOLF/s#HAVE_GRAYWOLF#$(HAVE_GRAYWOLF)#' \
+ -e '/HAVE_REPLACE/s#HAVE_REPLACE#$(HAVE_REPLACE)#' \
-e '/HAVE_OPENTIMER/s#HAVE_OPENTIMER#$(HAVE_OPENTIMER)#' \
-e '/HAVE_OPENSTA/s#HAVE_OPENSTA#$(HAVE_OPENSTA)#' \
+ -e '/HAVE_QROUTER/s#HAVE_QROUTER#$(HAVE_QROUTER)#' \
+ -e '/HAVE_MAGIC/s#HAVE_MAGIC#$(HAVE_MAGIC)#' \
+ -e '/HAVE_NETGEN/s#HAVE_NETGEN#$(HAVE_NETGEN)#' \
qflow.sh.in > qflow.sh
count_lvs.py: count_lvs.py.in
@@ -85,6 +116,9 @@ tksimpledialog.py: tksimpledialog.py.in
helpwindow.py: helpwindow.py.in
sed -e 's#ENV_PATH#$(ENV_PATH)#' $< > $@
+preproc.py: preproc.py.in
+ sed -e 's#ENV_PATH#$(ENV_PATH)#' $< > $@
+
count_lvs.py: count_lvs.py.in
sed -e 's#ENV_PATH#$(ENV_PATH)#' $< > $@
@@ -97,6 +131,12 @@ qflow_manager.py: qflow_manager.py.in
-e '/SUBST_BIN_DIR/s#SUBST_BIN_DIR#$(QFLOWEXECPATH)#' \
-e '/HAVE_OPENTIMER/s#HAVE_OPENTIMER#$(HAVE_OPENTIMER)#' \
-e '/HAVE_OPENSTA/s#HAVE_OPENSTA#$(HAVE_OPENSTA)#' \
+ -e '/HAVE_GRAYWOLF/s#HAVE_GRAYWOLF#$(HAVE_GRAYWOLF)#' \
+ -e '/HAVE_REPLACE/s#HAVE_REPLACE#$(HAVE_REPLACE)#' \
+ -e '/HAVE_QROUTER/s#HAVE_QROUTER#$(HAVE_QROUTER)#' \
+ -e '/HAVE_MAGIC/s#HAVE_MAGIC#$(HAVE_MAGIC)#' \
+ -e '/HAVE_NETGEN/s#HAVE_NETGEN#$(HAVE_NETGEN)#' \
+ -e '/HAVE_YOSYS/s#HAVE_YOSYS#$(HAVE_YOSYS)#' \
-e '/ENV_PATH/s#ENV_PATH#$(ENV_PATH)#' $< > $@
textreport.py: textreport.py.in
diff --git a/scripts/addspacers.tcl.in b/scripts/addspacers.tcl.in
index f2d11a8..fcd9d4a 100755
--- a/scripts/addspacers.tcl.in
+++ b/scripts/addspacers.tcl.in
@@ -31,6 +31,7 @@ if {$argc < 3} {
puts stdout "Usage: addspacers \[<options>\] <project_name> <lef_file> <fill_cell>"
puts stdout "Options:"
puts stdout " -stripe <width> <pitch> <pattern> \[-nostretch\] \[-techlef <tech.lef>\]"
+ puts stdout " \[-v <pwr_name>\] \[-g <gnd_name>\]"
exit 0
}
@@ -117,12 +118,12 @@ set dieyhigh 0
if [catch {open $lefname r} flef] {
puts stderr "Error: can't open LEF file $lefname for input"
- return
+ exit 1
}
if [catch {open $defname r} fdef] {
puts stderr "Error: can't open DEF file $defname for input"
- return
+ exit 1
}
if {$techlef != ""} {
@@ -153,8 +154,8 @@ close $fdef
set fdef [open $defname r]
# Multiply stripe width and pitch values by units to get output values
-set stripewidth [* $units $stripewidth]
-set stripepitch [* $units $stripepitch]
+set stripewidth [expr {round($units * $stripewidth)}]
+set stripepitch [expr {round($units * $stripepitch)}]
#----------------------------------------------------------------------
# Parse a viarule section for generating power bus connections
@@ -182,25 +183,25 @@ proc parse_viarule {leffile viarulename} {
# Pick up layername
continue
} elseif [regexp {[ \t]*RECT[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*;} $line lmatch llx lly urx ury] {
- set llx [* $llx $units]
- set lly [* $lly $units]
- set urx [* $urx $units]
- set ury [* $ury $units]
+ set llx [expr {round($llx * $units)}]
+ set lly [expr {round($lly * $units)}]
+ set urx [expr {round($urx * $units)}]
+ set ury [expr {round($ury * $units)}]
dict set viarulerec ${layername} [list $llx $lly $urx $ury $xspace $yspace]
} elseif [regexp {[ \t]*SPACING[ \t]+([^ \t]+)[ \t]+BY[ \t]+([^ \t]+)[ \t]*;} $line lmatch xspace yspace] {
- set xspace [* $xspace $units]
- set yspace [* $yspace $units]
+ set xspace [expr {round($xspace * $units)}]
+ set yspace [expr {round($yspace * $units)}]
dict set viarulerec ${layername} [list $llx $lly $urx $ury $xspace $yspace]
} elseif [regexp {[ \t]*METALOVERHANG[ \t]+([^ \t]+)[ \t]*;} $line lmatch overhang] {
# Ignore this.
continue
} elseif [regexp {[ \t]*OVERHANG[ \t]+([^ \t]+)[ \t]*;} $line lmatch overhang] {
# NOTE: Assuming METALOVERHANG is 0 and OVERHANG is on all sides.
- set overhang [* $overhang $units]
+ set overhang [expr {round($overhang * $units)}]
dict set viarulerec ${layername} [list $overhang $overhang]
} elseif [regexp {[ \t]*ENCLOSURE[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*;} $line lmatch xencl yencl] {
- set xencl [* $xencl $units]
- set yencl [* $yencl $units]
+ set xencl [expr {round($xencl * $units)}]
+ set yencl [expr {round($yencl * $units)}]
dict set viarulerec ${layername} [list $xencl $yencl]
} elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch viaruletest] {
if {"$viaruletest" != "$viarulename"} {
@@ -237,10 +238,10 @@ proc parse_via {leffile vianame} {
if [regexp {[ \t]*LAYER[ \t]+([^ \t]+)[ \t]*;} $line lmatch layername] {
continue
} elseif [regexp {[ \t]*RECT[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*;} $line lmatch llx lly urx ury] {
- set llx [* $llx $units]
- set lly [* $lly $units]
- set urx [* $urx $units]
- set ury [* $ury $units]
+ set llx [expr {round($llx * $units)}]
+ set lly [expr {round($lly * $units)}]
+ set urx [expr {round($urx * $units)}]
+ set ury [expr {round($ury * $units)}]
dict set viarec ${layername} [list $llx $lly $urx $ury]
} elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch viatest] {
if {"$viatest" != "$vianame"} {
@@ -266,6 +267,9 @@ proc parse_layer {leffile layername layernum} {
dict set layerrec name $layername
dict set layerrec number 0
+ # Diagnostic
+ puts stdout "Reading layer name $layername"
+
while {[gets $leffile line] >= 0} {
if [regexp {[ \t]*TYPE[ \t]+([^ \t]+)[ \t]*;} $line lmatch type] {
dict set layerrec type $type
@@ -279,15 +283,15 @@ proc parse_layer {leffile layername layernum} {
} elseif [regexp {[ \t]*DIRECTION[ \t]+([^ \t]+)[ \t]*;} $line lmatch direc] {
dict set layerrec direction $direc
} elseif [regexp {[ \t]*WIDTH[ \t]+([^ \t]+)[ \t]*;} $line lmatch width] {
- dict set layerrec width [* $width $units]
+ dict set layerrec width [expr {round($width * $units)}]
} elseif [regexp {[ \t]*SPACING[ \t]+([^ \t]+)[ \t]*;} $line lmatch space] {
dict set layerrec spacing [* $space $units]
} elseif [regexp {[ \t]*PITCH[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*;} $line \
lmatch xpitch ypitch] {
- dict set layerrec xpitch [* $xpitch $units]
- dict set layerrec ypitch [* $ypitch $units]
+ dict set layerrec xpitch [expr {round($xpitch * $units)}]
+ dict set layerrec ypitch [expr {round($ypitch * $units)}]
} elseif [regexp {[ \t]*PITCH[ \t]+([^ \t]+)[ \t]*;} $line lmatch pitch] {
- set pitch [* $pitch $units]
+ set pitch [expr {round($pitch * $units)}]
continue
} elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch layertest] {
if {"$layertest" != "$layername"} {
@@ -332,10 +336,10 @@ proc parse_port {pinname macroname leffile ox oy} {
set ${macroname}(${pinname},layer) $layernum
} elseif [regexp {[ \t]*RECT[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*;} \
$line lmatch llx lly urx ury] {
- set llx [* $llx $units]
- set lly [* $lly $units]
- set urx [* $urx $units]
- set ury [* $ury $units]
+ set llx [expr {round($llx * $units)}]
+ set lly [expr {round($lly * $units)}]
+ set urx [expr {round($urx * $units)}]
+ set ury [expr {round($ury * $units)}]
if {$llx < $ox} {set llx $ox}
if {$urx > $x2} {set urx $x2}
if {($llx == $ox) && ($urx == $x2)} {
@@ -423,10 +427,10 @@ proc parse_port {pinname macroname leffile ox oy} {
if {$testy > $ymax} {set ymax $testy}
}
- set llx [* $minx $units]
- set lly [* $ymin $units]
- set urx [* $maxx $units]
- set ury [* $ymax $units]
+ set llx [expr {round($minx * $units)}]
+ set lly [expr {round($ymin * $units)}]
+ set urx [expr {round($maxx * $units)}]
+ set ury [expr {round($ymax * $units)}]
set ${macroname}(${pinname},llx) $llx
set ${macroname}(${pinname},lly) $lly
@@ -486,6 +490,21 @@ proc skip_section {leffile sectionname} {
}
#----------------------------------------------------------------
+# Read through a NONDEFAULTRULE section, which can have layers
+# and vias which should be ignored.
+#----------------------------------------------------------------
+
+proc skip_nondefaultrule {leffile sectionname} {
+ while {[gets $leffile line] >= 0} {
+ if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] {
+ if {"$sectiontest" == "$sectionname"} {
+ return
+ }
+ }
+ }
+}
+
+#----------------------------------------------------------------
# Parse the macro contents of the LEF file and retain the information
# about cell size and pin positions.
#----------------------------------------------------------------
@@ -497,14 +516,14 @@ proc parse_macro {leffile macroname} {
if [regexp {[ \t]*SYMMETRY[ \t]+([^ \t]+)[ \t]*;} $line lmatch symmetry] {
set ${macroname}(symmetry) $symmetry
} elseif [regexp {[ \t]*ORIGIN[ \t]+(.+)[ \t]+(.+)[ \t]*;} $line lmatch x y] {
- set x [expr {int($x * $units)}]
- set y [expr {int($y * $units)}]
+ set x [expr {round($x * $units)}]
+ set y [expr {round($y * $units)}]
set ${macroname}(x) $x
set ${macroname}(y) $y
} elseif [regexp {[ \t]*SIZE[ \t]+(.+)[ \t]+BY[ \t]+(.+)[ \t]*;} \
$line lmatch w h] {
- set w [expr {int($w * $units)}]
- set h [expr {int($h * $units)}]
+ set w [expr {round($w * $units)}]
+ set h [expr {round($h * $units)}]
set ${macroname}(w) $w
set ${macroname}(h) $h
@@ -552,6 +571,8 @@ proc read_lef {flef fillcell} {
if {$newnum != 0} {set layernum $newnum}
} elseif [regexp {[ \t]*VIA[ \t]+([^ \t]+)} $line lmatch vianame] {
lappend vias [parse_via $flef $vianame]
+ } elseif [regexp {[ \t]*NONDEFAULTRULE[ \t]+([^ \t]+)} $line lmatch rulename] {
+ skip_nondefaultrule $flef $rulename
} elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] {
set result [parse_viarule $flef $viarulename]
if {$result != ""} {
@@ -639,6 +660,10 @@ if {[llength $fillcells] == 0} {
set testpitch 0
foreach layer $layers {
+ if [catch {dict get $layer type}] {
+ puts stderr "Warning: Layer {dict get $layer name} in techLEF file does not declare a type!"
+ continue
+ }
if {[dict get $layer type] != "ROUTING"} {continue}
set layerpitch [dict get $layer xpitch]
if {$layerpitch > 0} {
@@ -1363,6 +1388,8 @@ while {[gets $fdef line] >= 0} {
incr matches
} elseif [regexp {[ \t]*TRACKS} $line lmatch] {
incr matches
+ } elseif [regexp {[ \t]*ROW} $line lmatch] {
+ incr matches
} else {
puts stderr "Unexpected input in DEF file:"
puts stdout "Line is: $line"
@@ -1381,6 +1408,8 @@ foreach macro $fillcells {
}
set fillwidths [lsort -decreasing -index 1 $fillwidths]
+# puts stdout "Here are the fill cell widths: $fillwidths"
+
# For each row, add the width of the last cell in that row
# to get the row end X value
@@ -1468,8 +1497,9 @@ if {$dostripes} {
}
lappend stripefills [lindex [lindex $fillwidths $fidx] 0]
set xtot [+ $xtot $fmax]
- # puts stdout "Added to stripe filler [lindex [lindex $fillwidths $fidx] 0]"
- # puts stdout "Total stripe width now $xtot"
+
+ puts stdout "Added to stripe filler [lindex [lindex $fillwidths $fidx] 0]"
+ puts stdout "Total stripe width now $xtot"
}
# Reduce final stripe width to xtot so that it is equal to the
# best compatible fill size and is a multiple of the unit cell
@@ -1560,7 +1590,7 @@ set fdef [open $defname r]
if [catch {open $defoutname w} fanno] {
puts stderr "Error: can't open file $defoutname for output"
- return
+ exit 1
}
#-----------------------------------------------------------------
@@ -1611,17 +1641,18 @@ if {$dostripes && $dostretch} {
if [catch {open ${topname}.obs r} infobs] {
# No .obs file, no action needed.
puts stdout "No file ${topname}.obs, so no adjustment required."
- return
+ exit 0
}
if [catch {open ${topname}.obsx w} outfobs] {
puts stout "Error: Cannot open file ${topname}.obsx for writing!"
- return
+ exit 1
}
puts stdout "Adjusting obstructions for power striping"
# puts stdout "numstripes = $numstripes"
# puts stdout "stripewidth = $gwidth um"
# puts stdout "stripepitch = $gpitch um"
# puts stdout "stripeoffset = $goffset um"
+
while {[gets $infobs line] >= 0} {
if [regexp {[ \t]*obstruction[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]*$} $line \
lmatch xlow ylow xhigh yhigh layer] {
@@ -1665,10 +1696,10 @@ foreach obs $obslist {
set yhigh [lindex $obs 3]
if {($xlow != [lindex $obslast 0]) || ($ylow != [lindex $obslist 1]) ||
($xhigh == [lindex $obslast 2]) || ($yhigh != [lindex $obslist 3])} {
- set xlow [expr $xlow * $units]
- set ylow [expr $ylow * $units]
- set xhigh [expr $xhigh * $units]
- set yhigh [expr $yhigh * $units]
+ set xlow [expr {round($xlow * $units)}]
+ set ylow [expr {round($ylow * $units)}]
+ set xhigh [expr {round($xhigh * $units)}]
+ set yhigh [expr {round($yhigh * $units)}]
lappend newlist [list $xlow $ylow $xhigh $yhigh]
set obslast $obs
}
diff --git a/scripts/annotate.tcl.in b/scripts/annotate.tcl.in
index dcd0d08..7aa38cc 100755
--- a/scripts/annotate.tcl.in
+++ b/scripts/annotate.tcl.in
@@ -3,9 +3,8 @@
# annotate.tcl ---
#
# Read a file "antenna.out" produced by qrouter, and use this to
-# back-annotate the <project>.rtlnopwr.v verilog netlist for input
-# to static timing analysis, and the <project>.spc netlist for LVS
-# verification.
+# back-annotate the <project>.v verilog structural netlist for input
+# to static timing analysis.
#
# The "antenna.out" file contains connections made by the router to
# antenna anchoring cells. It is solely a product of the routing and
@@ -13,7 +12,7 @@
# stage prior to routing (e.g., the .blif or .cel files used for
# placement).
#
-# The output is written to the two filenames specified. Input and output
+# The output is written to the filename specified. Input and output
# files are opened at the same time, so do not specify the same file to
# be overwritten.
#
@@ -25,8 +24,8 @@
namespace path {::tcl::mathop ::tcl::mathfunc}
-if {$argc != 5 && $argc != 6 && $argc != 7} {
- puts stdout "Usage: annotate.tcl <antenna_file> <verilog_file> <spice_file> <verilog_out> <spice_out> [<spice_lib> [<pwrgnd_file>]]"
+if {$argc != 3 && $argc != 4} {
+ puts stdout "Usage: annotate.tcl <antenna_file> <verilog_file> <verilog_out> [<pwrgnd_file>]"
exit 0
}
@@ -34,22 +33,14 @@ puts stdout "Running annotate.tcl"
set antennaname [lindex $argv 0]
set vlogname [lindex $argv 1]
-set spicename [lindex $argv 2]
-set vlogoutname [lindex $argv 3]
-set spiceoutname [lindex $argv 4]
-
-if {$argc >= 6} {
- set spicelibname [lindex $argv 5]
-} else {
- set spicelibname ""
-}
+set vlogoutname [lindex $argv 2]
# If an additional file name is given, it should point to the file
# that sets vddnet and gndnet in the design (picked up from the LEF
# files, so no need to re-parse them.
-if {$argc == 7} {
- set pwrgndfile [lindex $argv 6]
+if {$argc == 4} {
+ set pwrgndfile [lindex $argv 3]
if [catch {open $pwrgndfile r} fpwr] {
puts stderr "Can't open file $pwrgndfile for input, assuming standard names."
@@ -153,195 +144,5 @@ while {[gets $fvlog line] >= 0} {
close $fvlog
close $fvout
-#-----------------------------------------------------------------
-# If vlogfile is the .rtlnopwr.v file, then do the equivalent
-# annotation to the .rtl.v file, if it exists.
-#-----------------------------------------------------------------
-
-set vlog2name [regsub {rtlnopwr} $vlogname rtl]
-if {${vlog2name} != ${vlogname}} {
-
- if [catch {open $vlog2name r} fvlog] {
- puts stderr "Error: can't open file $vlog2name for input"
- exit 1
- }
-
- set vlog2outname [regsub {rtlnopwr} $vlogoutname rtl]
- if [catch {open $vlog2outname w} fvout] {
- puts stderr "Error: can't open file $vlogout2name for output"
- exit 1
- }
-
- while {[gets $fvlog line] >= 0} {
- if [regexp {[ \t]*endmodule} $line lmatch] {
- # Insert the list here
- dict for {instname netchange} $changelist {
- set netname [lindex $netchange 0]
- set cellname [lindex $netchange 1]
- set pinname [lindex $netchange 2]
- if {${pinname} != "-" && ${pinname} != ""} {
- puts $fvout "${cellname} ${instname} ( .${vddnet}(${vddnet}), .${gndnet}(${gndnet}), .${pinname}(${netname}) );"
- } else {
- puts $fvout "${cellname} ${instname} ( .${vddnet}(${vddnet}), .${gndnet}(${gndnet}) );"
- }
- }
- }
- puts $fvout $line
- }
-
- close $fvlog
- close $fvout
-}
-
-#-----------------------------------------------------------------
-# If vlogfile is the .rtlnopwr.v file, then do the equivalent
-# annotation to the .rtlbb.v file, if it exists.
-#-----------------------------------------------------------------
-
-set vlog3name [regsub {rtlnopwr} $vlogname rtlbb]
-if {${vlog3name} != ${vlogname}} {
-
- if [catch {open $vlog3name r} fvlog] {
- puts stderr "Error: can't open file $vlog3name for input"
- exit 1
- }
-
- set vlog3outname [regsub {rtlnopwr} $vlogoutname rtlbb]
- if [catch {open $vlog3outname w} fvout] {
- puts stderr "Error: can't open file $vlogout3name for output"
- exit 1
- }
-
- while {[gets $fvlog line] >= 0} {
- if [regexp {[ \t]*endmodule} $line lmatch] {
- # Insert the list here
- dict for {instname netchange} $changelist {
- set netname [lindex $netchange 0]
- set cellname [lindex $netchange 1]
- set pinname [lindex $netchange 2]
- if {${pinname} != "-" && ${pinname} != ""} {
- puts $fvout "${cellname} ${instname} ( .${pinname}(${netname}) );"
- } else {
- puts $fvout "${cellname} ${instname} ( );"
- }
- }
- }
- puts $fvout $line
- }
-
- close $fvlog
- close $fvout
-}
-
-#-----------------------------------------------------------------
-# Open spice files for reading and writing
-#-----------------------------------------------------------------
-
-if [catch {open $spicename r} fspi] {
- puts stderr "Error: can't open file $spicename for input"
- exit 1
-}
-
-if {$spicelibname != ""} {
- if [catch {open $spicelibname r} fslib] {
- puts stderr "Error: can't open file $spicelibname for input"
- exit 1
- }
-}
-
-if [catch {open $spiceoutname w} fsout] {
- puts stderr "Error: can't open file $spiceoutname for output"
- exit 1
-}
-
-# Compile the list of antenna cells used (possibly there is only one)
-# Keep a pair of cell name and input pin name.
-
-set antlist {}
-dict for {instname netchange} $changelist {
- lappend antlist [list [lindex $netchange 1] [lindex $netchange 2]]
-}
-set antlist [lsort -uniq -index 0 $antlist]
-
-# Make a list of just the cells for use with lsearch.
-set antcells {}
-foreach pair $antlist {
- lappend antcells [lindex $pair 0]
-}
-
-# Read SPICE library to find the ".subckt" line for the antenna cell.
-
-while {[gets $fslib line] >= 0} {
- if [regexp -nocase {[ \t]*.subckt[ \t]+([^ \t]+)[ \t]+(.*)$} $line lmatch cellname pinlist] {
- set idx [lsearch $antcells $cellname]
- if {$idx >= 0} {
- puts "found cell $cellname pinlist $pinlist"
- set triplet [lindex $antlist $idx]
- lappend triplet $pinlist
- set antlist [lreplace $antlist $idx $idx $triplet]
- }
- }
-}
-
-if $debug {
- puts "antcells is $antcells"
- puts "antlist is $antlist"
-}
-
-close $fslib
-
-# Read SPICE netlist to the ".ends" line, and insert antenna cells.
-
-set instnames [dict create]
-
-while {[gets $fspi line] >= 0} {
- if [regexp -nocase {[ \t]*.ends} $line lmatch] {
- # Insert the list here
- dict for {instname netchange} $changelist {
- set netname [lindex $netchange 0]
- set cellname [lindex $netchange 1]
- set pinname [lindex $netchange 2]
-
- puts -nonewline $fsout "X${instname} "
- set idx [lsearch $antcells $cellname]
- set triplet [lindex $antlist $idx]
- set pinlist [lindex $triplet 2]
- set inpin [lindex $triplet 1]
-
- # For checking power/ground bus names: Note that names
- # in SPICE/CDL sometimes use a "!" global deliminer that
- # is missing in the LEF.
-
- foreach p $pinlist {
- if {$p == $inpin} {
- puts -nonewline $fsout "$netname "
- } elseif {$p == $vddnet || $p == "${vddnet}!"} {
- puts -nonewline $fsout "$vddnet "
- } elseif {$p == $gndnet || $p == "${gndnet}!"} {
- puts -nonewline $fsout "$gndnet "
- } else {
- # Warning: This makes the assumption that power and
- # ground names match those names used in the standard
- # cell set. Better to pass a file with names for
- # vddnet and gndnet.
- puts -nonewline $fsout "$p "
- }
- }
- puts $fsout "${cellname}"
- }
- puts $fsout $line
- } elseif [regexp -nocase {[ \t]*X([^ \t]+)[ \t]+} $line lmatch instname] {
- # In case of reapplying entries, remove the original ones
- if [catch {dict get $changelist $instname}] {
- puts $fsout $line
- }
- } else {
- puts $fsout $line
- }
-}
-
-close $fspi
-close $fsout
-
puts stdout "Done with annotate.tcl"
exit 0
diff --git a/scripts/arrangepins.tcl.in b/scripts/arrangepins.tcl.in
index 48175ee..7fd953d 100644
--- a/scripts/arrangepins.tcl.in
+++ b/scripts/arrangepins.tcl.in
@@ -229,13 +229,15 @@ while {[gets $fdef line] >= 0} {
} elseif [regexp {[ \t]*NEW[ \t]+[^ \t]+[ \t]+([^ \t]+)[ \t]+\([ \t]*(-?[0-9]+)[ \t]+-?[0-9]+[ \t]*\)[ \t]*\([ \t]*\*[ \t]+-?[0-9]+[ \t]*\)} $line lmatch width xmid] {
# Find the vertical stripes and register the X range of each
catch {
- set xranges [dict get $pinrec xranges]
- set hwid [/ $width 2]
- set xlow [- $xmid $hwid]
- set xhigh [+ $xmid $hwid]
- lappend xranges [list $xlow $xhigh]
- dict set pinrec xranges $xranges
- dict set specialrec $netname $pinrec
+ if {$xmid > 0} { ;# ignore metal1 comb buses on left
+ set xranges [dict get $pinrec xranges]
+ set hwid [/ $width 2]
+ set xlow [- $xmid $hwid]
+ set xhigh [+ $xmid $hwid]
+ lappend xranges [list $xlow $xhigh]
+ dict set pinrec xranges $xranges
+ dict set specialrec $netname $pinrec
+ }
}
}
} elseif $inpins {
@@ -723,6 +725,10 @@ if $debug {
# Find the X ranges along top and bottom where we want to avoid
# placing pins due to the presence of power supply stripe posts,
# which tend to get in the way of routes.
+#
+# Expand each range by one track pitch to make sure routes can
+# go vertically from the pin without creating a DRC spacing error
+# with the power post.
#-----------------------------------------------------------------
set xbranges {}
@@ -731,10 +737,10 @@ dict for {pinname pinrec} $specialrec {
catch {
set locxranges [dict get $pinrec xranges]
foreach xpair $locxranges {
- set xblow [/ [lindex $xpair 0] $botpitch]
- set xbhigh [/ [lindex $xpair 1] $botpitch]
- set xtlow [/ [lindex $xpair 0] $toppitch]
- set xthigh [/ [lindex $xpair 1] $toppitch]
+ set xblow [- [/ [lindex $xpair 0] $botpitch] 1]
+ set xbhigh [+ [/ [lindex $xpair 1] $botpitch] 1]
+ set xtlow [- [/ [lindex $xpair 0] $toppitch] 1]
+ set xthigh [+ [/ [lindex $xpair 1] $toppitch] 1]
lappend xbranges [list $xblow $xbhigh]
lappend xtranges [list $xtlow $xthigh]
}
diff --git a/scripts/checkdirs.sh.in b/scripts/checkdirs.sh.in
index 1d222d9..4a44e5c 100755
--- a/scripts/checkdirs.sh.in
+++ b/scripts/checkdirs.sh.in
@@ -57,32 +57,45 @@ if ($#argv != 1 && $#argv != 2) then
endif
set techname=${argv[1]}
+
+# Environment variable overrides the project root path in all cases
+# except when the project root path is specified on the command line
+# by -p. If the environment variable does not exist, the project
+# root directory is assumed to be the current working directory.
+
if ($#argv == 2) then
set projectpath=${argv[2]}
else
set projectpath=`pwd`
endif
+#-----------------------------------------------------------------
+# For portability, convert the home directory to the tilde escape
+# Note that projectpath must be quoted every time to preserve the ~
+#-----------------------------------------------------------------
+
+set projectpath="`echo $projectpath | sed -e 's,^${HOME},~,'`"
+
#----------------------------------------------------
# Check for standard working directories
#----------------------------------------------------
if ( -d ${projectpath}/source ) then
- set sourcedir=${projectpath}/source
+ set sourcedir="${projectpath}"/source
else
- set sourcedir=${projectpath}
+ set sourcedir="${projectpath}"
endif
if ( -d ${projectpath}/synthesis ) then
- set synthdir=${projectpath}/synthesis
+ set synthdir="${projectpath}"/synthesis
else
- set synthdir=${projectpath}
+ set synthdir="${projectpath}"
endif
if ( -d ${projectpath}/layout ) then
- set layoutdir=${projectpath}/layout
+ set layoutdir="${projectpath}"/layout
else
- set layoutdir=${projectpath}
+ set layoutdir="${projectpath}"
endif
#----------------------------------------------------
@@ -114,13 +127,13 @@ if ($techdir != "") then
endif
if ( -d ${projectpath}/tech/${techname} ) then
- set techdir=${projectpath}/tech/${techname}
+ set techdir="${projectpath}"/tech/${techname}
else
if ( -d ${projectpath}/${techname} ) then
- set techdir=${projectpath}/${techname}
+ set techdir="${projectpath}"/${techname}
else
if ( -d ${projectpath}/tech && -r ${projectpath}/tech/${techname}.sh ) then
- set techdir=${projectpath}/tech
+ set techdir="${projectpath}"/tech
else
if ( -d SUBST_TECH_DIR/${techname} ) then
set techdir=SUBST_TECH_DIR/${techname}
diff --git a/scripts/cleanup.sh b/scripts/cleanup.sh
index fa33102..051b7ce 100755
--- a/scripts/cleanup.sh
+++ b/scripts/cleanup.sh
@@ -11,7 +11,7 @@ if ($#argv < 2) then
endif
# Split out options from the main arguments (no options---this is a placeholder)
-set argline=(`getopt "" $argv[1-]`)
+set argline=(`getopt "p" $argv[1-]`)
set cmdargs=`echo "$argline" | awk 'BEGIN {FS = "-- "} END {print $2}'`
set argc=`echo $cmdargs | wc -w`
@@ -24,11 +24,18 @@ else
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
echo <source_name> is the root name of the verilog file, and
+ echo [options] are:
+ echo -p purge (keep sources only)
exit 1
endif
+set purge=0
+
foreach option (${argline})
switch (${option})
+ case -p:
+ set purge=1
+ breaksw
case --:
break
endsw
@@ -77,8 +84,15 @@ rm -f ${origname}_mapped_tmp.blif
rm -f ${origname}.clk
rm -f ${origname}.enc
rm -f ${origname}.init
+
+rm -f ${origname}_mapped.v.orig
+rm -f ${origname}_mapped.v
rm -f ${origname}_tmp.v
+if ( $purge == 1 ) then
+ rm -f ${origname}.ys
+endif
+
#----------------------------------------------------------
# Clean up files from synthesis. Leave the final buffered
# .blif netlist and the RTL verilog files
@@ -86,14 +100,34 @@ rm -f ${origname}_tmp.v
cd ${synthdir}
-# rm -f ${origname}.blif
-rm -f ${origname}_bak.blif
-rm -f ${origname}_tmp.blif
-rm -f ${rootname}_orig.blif
-rm -f ${rootname}_anno.blif
+rm -f ${origname}_bak.v
+rm -f ${origname}_tmp.v
+rm -f ${rootname}_orig.v
+rm -f ${rootname}_sized.v
+rm -f ${rootname}_mapped.v
+rm -f ${rootname}_anno.v
+rm -f ${rootname}_postroute.v
+rm -f ${rootname}_mapped.v
+rm -f ${rootname}.anno.v
rm -f ${rootname}_nofanout
-rm -f ${rootname}_powerground
rm -f tmp.blif
+rm -f tmp.v
+
+if ( $purge == 1 ) then
+ rm -f ${origname}.v
+ rm -f ${rootname}.spc
+ rm -f ${rootname}.xspice
+ rm -f ${rootname}.spef
+ rm -f ${rootname}.sdf
+ rm -f ${rootname}.dly
+ rm -f ${rootname}.rtl.v
+ rm -f ${rootname}.rtlbb.v
+ rm -f ${rootname}.rtlnopwr.v
+ rm -f ${rootname}_synth.rtl.v
+ rm -f ${rootname}_synth.rtlbb.v
+ rm -f ${rootname}_synth.rtlnopwr.v
+ rm -f ${rootname}_powerground
+endif
#----------------------------------------------------------
# Clean up the (excessively numerous) GrayWolf files
@@ -107,23 +141,54 @@ rm -f ${rootname}.blk ${rootname}.gen ${rootname}.gsav ${rootname}.history
rm -f ${rootname}.log ${rootname}.mcel ${rootname}.mdat ${rootname}.mgeo
rm -f ${rootname}.mout ${rootname}.mpin ${rootname}.mpth ${rootname}.msav
rm -f ${rootname}.mver ${rootname}.mvio ${rootname}.stat ${rootname}.out
-rm -f ${rootname}.mtmp ${rootname}.rc
-rm -f ${rootname}.pth ${rootname}.sav ${rootname}.scel
+rm -f ${rootname}.mtmp ${rootname}.pth ${rootname}.sav ${rootname}.scel
rm -f ${rootname}.txt ${rootname}.info
+rm -f *.ext
+
rm -f ${rootname}.pin ${rootname}.pl1 ${rootname}.pl2
-rm -f ${rootname}.cfg
rm -f antenna.out fillcells.txt fail.out
rm -f run_drc_map9v3.tcl
rm -f generate_gds_map9v3.tcl
rm -f migrate_map9v3.tcl
-# rm -f ${origname}_unroute.def
-
rm -f cn
rm -f failed
+if ( $purge == 1 ) then
+ rm -f comp.out
+ rm -f comp.json
+ rm -f qflow.magicrc
+ rm -f ${rootname}.spice
+ rm -f ${rootname}.cel
+ rm -f ${rootname}.cfg
+ rm -f ${rootname}.rc
+ rm -f ${rootname}.cel.bak
+ rm -f ${rootname}.lef
+ rm -f ${rootname}.def
+ rm -f ${rootname}_unroute.def
+ rm -f ${rootname}.mag
+ rm -f ${rootname}.obs
+ rm -f ${rootname}.gds
+ rm -f ${rootname}.par.orig
+endif
+
+cd ${logdir}
+
+if ( $purge == 1 ) then
+ rm -f drc.log
+ rm -f lvs.log
+ rm -f gdsii.log
+ rm -f migrate.log
+ rm -f place.log
+ rm -f post_sta.log
+ rm -f prep.log
+ rm -f route.log
+ rm -f sta.log
+ rm -f synth.log
+endif
+
#------------------------------------------------------------
# Done!
#------------------------------------------------------------
diff --git a/scripts/decongest.tcl.in b/scripts/decongest.tcl.in
index c257b01..875ef15 100755
--- a/scripts/decongest.tcl.in
+++ b/scripts/decongest.tcl.in
@@ -25,8 +25,10 @@
# add the fill cells into the DEF file after placement.
#
# "fillcells" is either a single name for a fill cell, or a
-# comma-separated list of three fill cells (of which one or two may
-# be empty strings) for plain fill, decap fill, and antenna fill.
+# comma-separated list of up to four fill cells (of which one or
+# two may be empty strings) for plain fill, decap fill, antenna
+# fill, and body ties (for standard cell sets that do not provide
+# substrate/well contacts inside the standard cells).
#
# "scale" gives a scalefactor which is the amount of
# increase in congestion that will cause an additional fill
@@ -48,17 +50,46 @@
# Option --units=<units> where units are distance units per micron
# in the .cel file (default 100)
#
+# Option --lib=<path>, where <path> is the full path to a
+# liberty format file, will parse the liberty file to determine
+# which cells are flops. Since flop cells have more area for
+# fewer pins, they tend not to be the point of congestion. So
+# density can be considerably optimized by placing far more
+# fill next to combinational cells and far less next to flops,
+# resulting in better routability at higher density.
+#
#------------------------------------------------------------
# Written by Tim Edwards, November 23, 2013
#------------------------------------------------------------
# LEF dimensions are microns unless otherwise stated.
#------------------------------------------------------------
+proc parse_liberty_file {flib} {
+ # Note that this is a trivial file parser. It only looks
+ # for lines of the type "cell (<name>)" followed by "ff (<...>)".
+
+ set floplist {}
+ set cellname ""
+ while {[gets $flib line] >= 0} {
+ if [regexp {^[ \t]*cell[ \t]*\([ \t]*([^ \t)]+)\)} $line lmatch cellname] {
+ # No action
+ }
+ if [regexp {^[ \t]*ff[ \t]*\([ \t]*[^)]+\)} $line lmatch] {
+ if {$cellname != ""} {
+ lappend floplist $cellname
+ }
+ }
+ }
+ close $flib
+ return $floplist
+}
+
# To-do: Just split the argument list into options and arguments,
# where options begin with "--". For now, just assume options come
# at the end.
set units 100
+set libertypath ""
while {true} {
set option [lindex $argv $argc-1]
@@ -74,6 +105,8 @@ while {true} {
}
switch -exact $optkey {
units {set units $optval}
+ lib {set libertypath $optval}
+ liberty {set libertypath $optval}
default {puts stderr "Unknown option switch $option"}
}
} else {
@@ -86,9 +119,10 @@ if {$argc < 4 || $argc > 7} {
puts stderr "Usage: decongest.tcl <rootname> <leffile> <fillcell> [<scale> <offset>]"
puts stderr "or: decongest.tcl <rootname> <leffile> <fillcell> <density>"
puts stderr "or: decongest.tcl <rootname> <leffile> <fillcells> <density> <ratios>"
- puts stderr "<fillcell> is a comma-separated list of up to three fill cell types."
+ puts stderr "<fillcell> is a comma-separated list of up to four fill cell types."
puts stderr "<ratios> is a space-separated list of ratios for each fill cell type."
puts stderr "use option --units=<units> to set units other than centimicrons."
+ puts stderr "use option --lib=<path> to analyze a liberty file for optimizing."
exit 1
}
@@ -103,6 +137,7 @@ set cinfofile ${cellname}.cinfo
set lefname [lindex $argv 1]
set filltypes [split [lindex $argv 2] ,]
set fillratios {}
+set nopadcells {}
if {$argc == 4} {
set density [lindex $argv 3]
@@ -123,12 +158,11 @@ if {$argc == 4} {
set tmpfillr [split $offset ,]
for {set i 0} {$i < [llength $tmpfillr]} {incr i} {
set rfill [lindex $tmpfillr $i]
- if {$rfill == {}} {set rfill 0}
lappend fillratios $rfill
}
}
} else {
- # For 6 or 7 arguments. Note that there is no 5-argument syntax
+ # For 6 to 8 arguments. Note that there is no 5-argument syntax
# for ratios, since if only one fill type is specified, then by
# definition it must be 100% of the fill.
@@ -161,6 +195,15 @@ if [catch {open $annofile w} fanno] {
return
}
+if {$libertypath != ""} {
+ if [catch {open $libertypath r} flib] {
+ puts stderr "Warning: can't open file $libertypath for input, will not optimize"
+ } else {
+ set nopadcells [parse_liberty_file $flib]
+ # puts stdout "Diagnostic: List of flop cells is $nopadcells"
+ }
+}
+
#----------------------------------------------------------------
# Check filltypes and determine how many are requested. This
# code is complicated mostly to allow flexibility in the way
@@ -173,15 +216,15 @@ foreach filltype $filltypes {
if {$filltype != ""} {incr num_filltypes}
}
-if {$num_filltypes > 3} {
- puts stderr "Can only handle up to three fill cell types. Ignoring the rest."
- set filltypes [lrange $filltypes 0 2]
+if {$num_filltypes > 4} {
+ puts stderr "Can only handle up to four fill cell types. Ignoring the rest."
+ set filltypes [lrange $filltypes 0 3]
} elseif {$filltypes == {}} {
puts stderr "Warning: No fill cell types specified."
}
# If filltypes is truncated, then pad the array. However, it is
-# much preferable for the caller to supply all three entries,
+# much preferable for the caller to supply all four entries,
# with empty strings for those that don't exist, because the
# following method does not try to match fill cell names to fill
# cell types.
@@ -189,66 +232,162 @@ if {$num_filltypes > 3} {
if {[llength $filltypes] == 1} {
lappend filltypes ""
lappend filltypes ""
+ lappend filltypes ""
} elseif {[llength $filltypes] == 2} {
lappend filltypes ""
+ lappend filltypes ""
+} elseif {[llength $filltypes] == 3} {
+ lappend filltypes ""
}
-# Automatically assign fill ratios if ratios were not specified.
+if {[llength $filltypes] == 4} {
-if {$fillratios == {}} {
- if {$num_filltypes == 1} {
- foreach filltype $filltypes {
- if {$filltype != ""} {
- lappend fillratios 100
- } else {
- lappend fillratios 0
- }
- }
- } elseif {$num_filltypes == 2} {
- foreach filltype $filltypes {
- if {$filltype != ""} {
- lappend fillratios 50
- } else {
- lappend fillratios 0
- }
+ # "needfill" array marks which fill types have non-zero fill ratios.
+ set needfill {}
+ foreach filltype $filltypes {
+ if {$filltype != ""} {
+ lappend needfill 1
+ } else {
+ lappend needfill 0
}
- } else {
- set fillratios {34 33 33}
}
-}
-if {[llength $filltypes] == 3} {
- if {[llength $fillratios] > 3} {
- puts stderr "Error: Too many fill ratios. Ignoring all but the first three."
- set fillratios [lrange $fillratios 0 2]
+ if {[llength $fillratios] > 4} {
+ puts stderr "Error: Too many fill ratios. Ignoring all but the first four."
+ set fillratios [lrange $fillratios 0 3]
}
- if {[llength $fillratios] == 3} {
- set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1] + [lindex $fillratios 2]}]
- if {$addr > 100} {
- puts stderr "Error: Fill ratios sum to greater than 100 percent."
- set fillratios [lrange $fillratios 0 1]
+ set allratios {}
+ set validratios {}
+ foreach ratio $fillratios {
+ if {$ratio == {} || $ratio == ""} {
+ lappend validratios 0
+ lappend allratios 0
+ } else {
+ lappend validratios 1
+ if {[catch {set iratio [expr {int($ratio * 1)}]}]} {
+ puts stderr "Error: Ratio $ratio is not a number"
+ lappend allratios 0
+ } else {
+ lappend allratios $iratio
+ }
+ }
+ }
+ while {[llength $allratios] < 4} {
+ lappend allratios 0
+ lappend validratios 0
+ }
+
+ set filldiff 0
+ set addfill {}
+ for {set i 0} {$i < 4} {incr i} {
+ if {[lindex $validratios $i] == 0 && [lindex $needfill $i] == 1} {
+ lappend addfill 1
+ incr filldiff
+ } else {
+ lappend addfill 0
}
}
- if {[llength $fillratios] == 2} {
- set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1]}]
+ set f1 [expr {[lindex $allratios 0] * [lindex $needfill 0]}]
+ set f2 [expr {[lindex $allratios 1] * [lindex $needfill 1]}]
+ set f3 [expr {[lindex $allratios 2] * [lindex $needfill 2]}]
+ set f4 [expr {[lindex $allratios 3] * [lindex $needfill 3]}]
+ set addr [expr {$f1 + $f2 + $f3 + $f4}]
+
+ # If the total of specified fill ratios for the valid fill
+ # types is more than 100, then proportionally reduce the ratios
+ # to 100
+
+ if {$addr > 100} {
+ puts stderr "Error: Fill ratios sum to greater than 100 percent."
+ set allratios [lreplace $allratios 3 3 0]
+ set addr [expr {$f1 + $f2 + $f3}]
if {$addr > 100} {
- puts stderr "Error: First two fill ratios sum to greater than 100 percent."
- set fillratios [lindex $fillratios 0]
- } else {
- lappend fillratios [expr {100 - [lindex $fillratios 0] - [lindex $fillratios 1]}]
- lappend fillratios [expr {100 - $addr}]
+ set allratios [lreplace $allratios 2 2 0]
+ set addr [expr {$f1 + $f2}]
+ if {$addr > 100} {
+ set allratios [lreplace $allratios 1 1 0]
+ set addr $f1
+ if {$addr > 100} {
+ set allratios [lreplace $allratios 0 0 100]
+ }
+ }
}
}
- if {[llength $fillratios] == 1} {
- puts stderr "Error: Only one fill ratio. Evenly splitting the remainder."
- set remain [expr {int((100 - [lindex $fillratios 0]) / 2)}]
- lappend fillratios $remain
- set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1]}]
- lappend fillratios [expr {100 - $addr}]
+ # Recompute the ratio total
+
+ set f1 [expr {[lindex $allratios 0] * [lindex $needfill 0]}]
+ set f2 [expr {[lindex $allratios 1] * [lindex $needfill 1]}]
+ set f3 [expr {[lindex $allratios 2] * [lindex $needfill 2]}]
+ set f4 [expr {[lindex $allratios 3] * [lindex $needfill 3]}]
+ set addr [expr {$f1 + $f2 + $f3 + $f4}]
+
+ # If the number of fill ratios specified is less than the number of
+ # valid fill types and sums to less than 100, then automatically
+ # add the remaining ratio to the unspecified cells.
+
+ set fillamounts {}
+ if {$filldiff == 1} {
+ lappend fillamounts [expr {100 - $addr}]
+ } elseif {$filldiff == 2} {
+ set rval [expr {int((100 - $addr) / 2)}]
+ lappend fillamounts $rval
+ lappend fillamounts [expr {100 - $addr - $rval}]
+ } elseif {$filldiff == 3} {
+ set rval [expr {int((100 - $addr) / 3)}]]
+ lappend fillamounts $rval
+ lappend fillamounts [expr {100 - $addr - 2 * $rval}]
+ } elseif {$filldiff == 4} {
+ set fillamounts {25 25 25 25}
}
+
+ for {set i 0} {$i < 4} {incr i} {
+ if {[lindex $addfill $i] == 1} {
+ set allratios [lreplace $allratios $i $i [lindex $fillamounts 0]]
+ set fillamounts [lrange $fillamounts 1 end]
+ }
+ }
+
+ # Recompute the ratio total
+
+ set f1 [expr {[lindex $allratios 0] * [lindex $needfill 0]}]
+ set f2 [expr {[lindex $allratios 1] * [lindex $needfill 1]}]
+ set f3 [expr {[lindex $allratios 2] * [lindex $needfill 2]}]
+ set f4 [expr {[lindex $allratios 3] * [lindex $needfill 3]}]
+ set addr [expr {$f1 + $f2 + $f3 + $f4}]
+
+ # If the total of specified fill ratios for the valid fill
+ # types is less than 100, then proportionally increase the ratios
+ # to 100
+
+ set addto {}
+ set dist 0
+ for {set i 0} {$i < 4} {incr i} {
+ if {[lindex $allratios $i] > 0 && [lindex $needfill $i] > 0} {
+ lappend addto 1
+ incr dist
+ } else {
+ lappend addto 0
+ }
+ }
+ set addin [expr {int((100 - $addr) / $dist)}]
+ set remain [expr {(100 - $addr) - ($dist * $addin)}]
+
+ for {set i 0} {$i < 4} {incr i} {
+ if {[lindex $addto $i] == 1} {
+ set oldval [lindex $allratios $i]
+ set newval [expr {$oldval + $addin}]
+ if {$remain > 0} {
+ set newval [expr {$newval + $remain}]
+ set remain 0
+ }
+ set allratios [lreplace $allratios $i $i $newval]
+ }
+ }
+
+ set fillratios $allratios
}
#----------------------------------------------------------------
@@ -313,10 +452,12 @@ flush stdout
set fillcells {}
set decapcells {}
set antennacells {}
+set bodytiecells {}
set fillcell [lindex $filltypes 0]
set decapcell [lindex $filltypes 1]
set antennacell [lindex $filltypes 2]
+set bodytiecell [lindex $filltypes 3]
while {[gets $flef line] >= 0} {
if [regexp {[ \t]*MACRO[ \t]+(.+)[ \t]*$} $line lmatch macroname] {
@@ -377,13 +518,14 @@ while {[gets $flef line] >= 0} {
# whole lot we can do. . .
set totalfills [expr {[llength $fillcells] + [llength $decapcells] + \
- [llength $antennacells]}]
+ [llength $antennacells] + [llength $bodytiecells]}]
set usedfills [expr {[llength $fillcells] * [lindex $fillratios 0] + \
[llength $decapcells] * [lindex $fillratios 1] + \
- [llength $antennacells] * [lindex $fillratios 2]}]
+ [llength $antennacells] * [lindex $fillratios 2] + \
+ [llength $bodytiecells] * [lindex $fillratios 3]}]
if {$totalfills == 0} {
- puts stdout "No fill cells (${fillcell}, ${decapcell}, ${antennacell}) found in macro file ${lefname}!"
+ puts stdout "No fill cells (${fillcell}, ${decapcell}, ${antennacell}, ${bodytiecell}) found in macro file ${lefname}!"
close $flef
exit 1
}
@@ -393,6 +535,7 @@ if {$usedfills == 0} {
if {[llength $fillcells] > 0} {lappend foundcells $fillcell}
if {[llength $decapcells] > 0} {lappend foundcells $decapcell}
if {[llength $antennacells] > 0} {lappend foundcells $antennacell}
+ if {[llength $bodytiecells] > 0} {lappend foundcells $bodytiecell}
puts stdout "Fill cells found are: $foundcells"
close $flef
exit 1
@@ -400,6 +543,12 @@ if {$usedfills == 0} {
close $flef
+# Add all the fill cells to the list of cells that are not padded.
+lappend nopadcells {*}$fillcells
+lappend nopadcells {*}$decapcells
+lappend nopadcells {*}$antennacells
+lappend nopadcells {*}$bodytiecells
+
# Sort each array of fill cells by width
set fillwidths {}
@@ -429,6 +578,15 @@ set antennainfo [lindex $antennawidths 0]
set antennamacro [lindex $antennainfo 0]
set antennavalue [lindex $antennainfo 1]
+set bodytiewidths {}
+foreach macro $bodytiecells {
+ lappend bodytiewidths [list $macro [subst \$${macro}(w)]]
+}
+set bodytiewidths [lsort -decreasing -index 1 -real $bodytiewidths]
+set bodytieinfo [lindex $bodytiewidths 0]
+set bodytiemacro [lindex $bodytieinfo 0]
+set bodytievalue [lindex $bodytieinfo 1]
+
#------------------------------------------------------------------------
# Now read the contents of the cinfo file so that we have a list of the
# cells to add padding to
@@ -490,10 +648,20 @@ if {$scale > 0.0} {
set totalwidth 0
set instlist {}
+ set nopadlist {}
while {[gets $fcel line] >= 0} {
if [regexp {[ \t]*cell[ \t]*([0-9]+)[ \t]+([^ \t]+)} $line \
lmatch instnum instname] {
- lappend instlist $instname
+ # NOTE: The .cel file has names in the format <cellname>:<instname>
+ if [regexp {([^:]+):[^:]+} $instname lmatch cellname] {
+ if {[lsearch $nopadcells $cellname] >= 0} {
+ lappend nopadlist $instname
+ } else {
+ lappend instlist $instname
+ }
+ } else {
+ lappend instlist $instname
+ }
gets $fcel line
regexp {[ \t]*left[ \t]+([-]*[0-9]+)[ \t]+right[ \t]+([-]*[0-9]+)} $line \
lmatch left right
@@ -511,9 +679,10 @@ if {$scale > 0.0} {
set filltotal [expr {int($alltotal * [lindex $fillratios 0] / 100)}]
set decaptotal [expr {int($alltotal * [lindex $fillratios 1] / 100)}]
- set antennatotal [expr {int($alltotal - ($filltotal + $decaptotal))}]
+ set antennatotal [expr {int($alltotal * [lindex $fillratios 2] / 100)}]
+ set bodytietotal [expr {int($alltotal * [lindex $fillratios 3] / 100)}]
- set numcells [llength $instlist]
+ set numcells [expr {[llength $instlist] + [llength $nopadlist]}]
# Diagnostic information
puts stdout ""
@@ -522,7 +691,8 @@ if {$scale > 0.0} {
puts stdout "Number of cells = $numcells, total width = $totalwidth"
puts stdout "Width of fill = $filltotal"
puts stdout "Width of decap = $decaptotal"
- puts stdout "Width of antenna = $antennatotal"
+ puts stdout "Width of antenna = $antennatotal"
+ puts stdout "Width of body ties = $bodytietotal"
puts stdout ""
# Rewind the cel file for the next step
@@ -562,19 +732,76 @@ if {$scale > 0.0} {
# Target antenna cell width = antennatotal
# Target antenna cell ratio = antennaratio = (antennatotal / totalwidth)
# Aggregate antenna cell width = antennaagg
+# Target body tie cell width = bodytietotal
+# Target body tie cell ratio = bodytieratio = (bodytietotal / totalwidth)
+# Aggregate body tie cell width = bodytieagg
#
#------------------------------------------------------------------------
set fillratio [expr {(0.0 + $filltotal) / $totalwidth}]
set decapratio [expr {(0.0 + $decaptotal) / $totalwidth}]
set antennaratio [expr {(0.0 + $antennatotal) / $totalwidth}]
-
-set fillagg 0
-set decapagg 0
-set antennaagg 0
+set bodytieratio [expr {(0.0 + $bodytietotal) / $totalwidth}]
set aggwidth 0
+# Find the total number of instances to which fill will be attached
+set numinsts [llength $instlist]
+
+# Find the ideal exact amount of fill to add per instance.
+set fillperinst [expr {(0.0 + $filltotal) / (0.0 + $numinsts)}]
+set decapperinst [expr {(0.0 + $decaptotal) / (0.0 + $numinsts)}]
+set antennaperinst [expr {(0.0 + $antennatotal) / (0.0 + $numinsts)}]
+set bodytieperinst [expr {(0.0 + $bodytietotal) / (0.0 + $numinsts)}]
+
+# Track the ideal amount of fill that should be added at any point in the output
+set filltotalideal 0.0
+set decaptotalideal 0.0
+set antennatotalideal 0.0
+set bodytietotalideal 0.0
+
+# Track the actual amount of fill that has been added at any point in the output
+set filltotalactual 0
+set decaptotalactual 0
+set antennatotalactual 0
+set bodytietotalactual 0
+
+set numfilltypes [llength $fillwidths]
+if {$numfilltypes > 0} {
+ set lastfillindex [expr {$numfilltypes - 1}]
+ set lastfillentry [lindex $fillwidths $lastfillindex]
+ set minfillwidth [lindex $lastfillentry 1]
+} else {
+ set minfillwidth 100 ;# any value above zero should do
+}
+
+set numdecaptypes [llength $decapwidths]
+if {$numdecaptypes > 0} {
+ set lastdecapindex [expr {$numdecaptypes - 1}]
+ set lastdecapentry [lindex $decapwidths $lastdecapindex]
+ set mindecapwidth [lindex $lastdecapentry 1]
+} else {
+ set mindecapwidth 100 ;# any value above zero should do
+}
+
+set numantennatypes [llength $antennawidths]
+if {$numantennatypes > 0} {
+ set lastantennaindex [expr {$numantennatypes - 1}]
+ set lastantennaentry [lindex $antennawidths $lastantennaindex]
+ set minantennawidth [lindex $lastantennaentry 1]
+} else {
+ set minantennawidth 100 ;# any value above zero should do
+}
+
+set numbodytietypes [llength $bodytiewidths]
+if {$numbodytietypes > 0} {
+ set lastbodytieindex [expr {$numbodytietypes - 1}]
+ set lastbodytieentry [lindex $bodytiewidths $lastbodytieindex]
+ set minbodytiewidth [lindex $lastbodytieentry 1]
+} else {
+ set minbodytiewidth 100 ;# any value above zero should do
+}
+
while {[gets $fcel line] >= 0} {
if [regexp {[ \t]*cell[ \t]*([0-9]+)[ \t]+([^ \t]+)} \
$line lmatch instnum instname] {
@@ -585,18 +812,31 @@ while {[gets $fcel line] >= 0} {
$cline lmatch left right rest] {
puts $fanno $line ;# failed to parse, so output unchanged
puts $fanno $cline
- } else {
- # Calculate aggregate cell width
+ } elseif {$instidx < 0} {
+ puts $fanno $line ;# unknown/unhandled cell, so output unchanged
+ puts $fanno $cline
+
+ } else {
+ # Calculate cell width
set cellwidth [expr {$right - $left}]
- # puts stdout "Diagnostic: instance $instname cell width = $cellwidth"
- incr aggwidth $cellwidth
- if {$instidx < 0} {
- puts $fanno $line ;# unknown cell, so output unchanged
- puts $fanno $cline
- } else {
- set needfill [expr {$aggwidth * $fillratio}]
- set cstr ""
+
+ # Update the ideal fill total.
+ set filltotalideal [expr {$filltotalideal + $fillperinst}]
+ set decaptotalideal [expr {$decaptotalideal + $decapperinst}]
+ set antennatotalideal [expr {$antennatotalideal + $antennaperinst}]
+ set bodytietotalideal [expr {$bodytietotalideal + $bodytieperinst}]
+
+ set needfill [expr {$filltotalideal - $filltotalactual}]
+ set needdecap [expr {$decaptotalideal - $decaptotalactual}]
+ set needantenna [expr {$antennatotalideal - $antennatotalactual}]
+ set needbodytie [expr {$bodytietotalideal - $bodytietotalactual}]
+
+ set cstr ""
+
+ # If needfill is larger than half the minimum fill width, then add fill
+ if {$needfill > [expr {$minfillwidth / 2}]} {
+ set fillagg 0
set lastfill ""
set lastwid 0
while {$needfill > $fillagg} {
@@ -611,6 +851,7 @@ while {[gets $fcel line] >= 0} {
if {$cwid == 0} {break}
incr fillagg $cwid
incr cellwidth $cwid
+ incr filltotalactual $cwid
# puts stdout "Diagnostic: add fill width = $cwid"
# puts stdout "Diagnostic: new cell width = $cellwidth"
if {$lastfill == ""} {
@@ -637,8 +878,11 @@ while {[gets $fcel line] >= 0} {
set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
}
}
+ }
- set needdecap [expr {$aggwidth * $decapratio}]
+ # If needdecap is larger than half the minimum decap width, then add decap
+ if {$needdecap > [expr {$mindecapwidth / 2}]} {
+ set decapagg 0
set lastfill ""
set lastwid 0
while {$needdecap > $decapagg} {
@@ -653,6 +897,7 @@ while {[gets $fcel line] >= 0} {
if {$cwid == 0} {break}
incr decapagg $cwid
incr cellwidth $cwid
+ incr decaptotalactual $cwid
# puts stdout "Diagnostic: add decap width = $cwid"
# puts stdout "Diagnostic: new cell width = $cellwidth"
if {$lastfill == ""} {
@@ -679,8 +924,11 @@ while {[gets $fcel line] >= 0} {
set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
}
}
+ }
- set needantenna [expr {$aggwidth * $antennaratio}]
+ # If needantenna is larger than half the minimum antenna width, then add antenna
+ if {$needantenna > [expr {$minantennawidth / 2}]} {
+ set antennaagg 0
set lastfill ""
set lastwid 0
while {$needantenna > $antennaagg} {
@@ -695,6 +943,7 @@ while {[gets $fcel line] >= 0} {
if {$cwid == 0} {break}
incr antennaagg $cwid
incr cellwidth $cwid
+ incr antennatotalactual $cwid
# puts stdout "Diagnostic: add antenna width = $cwid"
# puts stdout "Diagnostic: new cell width = $cellwidth"
if {$lastfill == ""} {
@@ -721,14 +970,60 @@ while {[gets $fcel line] >= 0} {
set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
}
}
-
- puts $fanno "cell $instnum ${cstr}${instname}"
+ }
- # puts stdout "Diagnostic: final cell width = $cellwidth"
- set fleft [expr {-($cellwidth / 2)}]
- set fright [expr {$fleft + $cellwidth}]
- puts $fanno "left $fleft right $fright $rest"
+ # If needbodytie is larger than half the minimum bodytie width, then add bodytie
+ if {$needbodytie > [expr {$minbodytiewidth / 2}]} {
+ set bodytieagg 0
+ set lastfill ""
+ set lastwid 0
+ while {$needbodytie > $bodytieagg} {
+ set cwid 0
+ foreach bodytieinfo $bodytiewidths {
+ set cmac [lindex $bodytieinfo 0]
+ set cwid [lindex $bodytieinfo 1]
+ if {[expr {$bodytieagg + $cwid}] < $needbodytie} {
+ break
+ }
+ }
+ if {$cwid == 0} {break}
+ incr bodytieagg $cwid
+ incr cellwidth $cwid
+ incr bodytietotalactual $cwid
+ # puts stdout "Diagnostic: add body tie width = $cwid"
+ # puts stdout "Diagnostic: new cell width = $cellwidth"
+ if {$lastfill == ""} {
+ set lastfill $cmac
+ set lastwid $cwid
+ set multiplier 1
+ } elseif {$lastfill == $cmac} {
+ incr multiplier
+ } else {
+ if {$multiplier == 1} {
+ set cstr ${lastfill}.${lastwid}.${cstr}
+ } else {
+ set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
+ }
+ set lastfill $cmac
+ set lastwid $cwid
+ set multiplier 1
+ }
+ }
+ if {$lastfill != ""} {
+ if {$multiplier == 1} {
+ set cstr ${lastfill}.${lastwid}.${cstr}
+ } else {
+ set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
+ }
+ }
}
+
+ puts $fanno "cell $instnum ${cstr}${instname}"
+
+ # puts stdout "Diagnostic: final cell width = $cellwidth"
+ set fleft [expr {-($cellwidth / 2)}]
+ set fright [expr {$fleft + $cellwidth}]
+ puts $fanno "left $fleft right $fright $rest"
}
} else {
puts $fanno $line
diff --git a/scripts/getantennacell.tcl.in b/scripts/getantennacell.tcl.in
index b2de36d..b4a7fde 100755
--- a/scripts/getantennacell.tcl.in
+++ b/scripts/getantennacell.tcl.in
@@ -50,6 +50,10 @@ proc skip_section {leffile sectionname} {
#----------------------------------------------------------------
# Parse the pin contents of the LEF file macro
+#
+# To cope with LEF macro sets where a signal pin may not have a
+# use declared, return the pin name of any pin not having a USE
+# instead of true/false.
#----------------------------------------------------------------
proc parse_pin {leffile pinname} {
@@ -69,6 +73,9 @@ proc parse_pin {leffile pinname} {
}
}
}
+ if {$portuse == ""} {
+ set retval $pinname ;# special case
+ }
return $retval
}
@@ -80,6 +87,7 @@ proc parse_pin {leffile pinname} {
proc parse_macro {leffile macroname} {
global $macroname units
+ set savepin ""
while {[gets $leffile line] >= 0} {
if [regexp {[ \t]*SYMMETRY[ \t]+(.+)[ \t]*;} $line lmatch symmetry] {
set ${macroname}(symmetry) $symmetry
@@ -97,9 +105,12 @@ proc parse_macro {leffile macroname} {
} elseif [regexp {[ \t]*PIN[ \t]+(.+)[ \t]*$} $line lmatch pinname] {
# Antenna cell has only one pin, so just set a record "pin" for each macro
- if {[parse_pin $leffile $pinname]} {
- set ${macroname}(pin) $pinname
- }
+ set presult [parse_pin $leffile $pinname]
+ if {$presult == true} {
+ set ${macroname}(pin) $pinname
+ } elseif {$presult != false} {
+ set savepin $pinname
+ }
} elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch macrotest] {
if {"$macrotest" == "$macroname"} {
break
@@ -108,6 +119,15 @@ proc parse_macro {leffile macroname} {
}
}
}
+
+ # Check for unmarked signal pin.
+ if [catch {set ${macroname}(pin)}] {
+ if {$savepin != ""} {
+ set ${macroname}(pin) $savepin
+ } else {
+ set ${macroname}(pin) ERROR ;# Flag this
+ }
+ }
}
#-----------------------------------------------------------------
@@ -119,16 +139,21 @@ flush stdout
set useantennacell {}
set useantennapin {}
+set useantennaw 0
while {[gets $flef line] >= 0} {
if [regexp {[ \t]*MACRO[ \t]+(.+)[ \t]*$} $line lmatch macroname] {
# Parse the "macro" statement
parse_macro $flef $macroname
if {[regexp "^$antennacell" $macroname] == 1} {
- # Check width against feedthrough width
- # puts stdout "Diagnostic: macro $macroname input [subst \$${macroname}(pin)]"
- set useantennacell $macroname
- set useantennapin [subst \$${macroname}(pin)]
+ set w [subst \$${macroname}(w)]
+ if {($useantennacell == "") || ($w < $useantennaw)} {
+ # To do: Check width against feedthrough width
+ puts stdout "Diagnostic: macro $macroname input [subst \$${macroname}(pin)]"
+ set useantennacell $macroname
+ set useantennapin [subst \$${macroname}(pin)]
+ set useantennaw $w
+ }
}
} elseif [regexp {[ \t]*LAYER[ \t]+([^ \t]+)} $line lmatch layername] {
skip_section $flef $layername
diff --git a/scripts/getfillcell.tcl.in b/scripts/getfillcell.tcl.in
index 4550e7b..e0db020 100755
--- a/scripts/getfillcell.tcl.in
+++ b/scripts/getfillcell.tcl.in
@@ -124,15 +124,21 @@ flush stdout
puts stdout "Diagnostic: fill cell width from .par file is $fwidth"
set usefillcell {}
+set feedcellw 0
+set feedcellmin {}
while {[gets $flef line] >= 0} {
if [regexp {[ \t]*MACRO[ \t]+(.+)[ \t]*$} $line lmatch macroname] {
# Parse the "macro" statement
parse_macro $flef $macroname
if {[regexp "^$fillcell" $macroname] == 1} {
# Check width against feedthrough width
- # puts stdout "Diagnostic: macro $macroname width = [subst \$${macroname}(w)]"
- if {[subst \$${macroname}(w)] == $fwidth} {
+ set w [subst \$${macroname}(w)]
+ puts stdout "Diagnostic: macro $macroname width = $w"
+ if {$w == $fwidth} {
set usefillcell $macroname
+ } elseif {($w < $feedcellw) || ($feedcellw == 0)} {
+ set feedcellw $w
+ set feedcellmin $macroname
}
}
} elseif [regexp {[ \t]*LAYER[ \t]+([^ \t]+)} $line lmatch layername] {
@@ -176,6 +182,16 @@ while {[gets $flef line] >= 0} {
}
}
+if {$usefillcell == {}} {
+ if {$feedcellmin != {}} {
+ puts stderr "Warning: No fill cells correspond to cell width in the .par file."
+ puts stderr "Using smallest matching fill macro $feedcellmin"
+ set usefillcell $feedcellmin
+ } else {
+ puts stderr "Error: No matching fill cells found in LEF macro file."
+ }
+}
+
close $flef
close $fpar
diff --git a/scripts/placement.sh b/scripts/graywolf.sh
index 73e3068..25500c7 100755
--- a/scripts/placement.sh
+++ b/scripts/graywolf.sh
@@ -1,10 +1,8 @@
#!/usr/bin/tcsh -f
#----------------------------------------------------------
# Placement script using GrayWolf
+# Prerequisites: Mapped verilog netlist from synthesis.
#
-# This script assumes the existence of the pre-GrayWolf
-# ".cel" and ".par" files. It will run GrayWolf for the
-# placement.
#----------------------------------------------------------
# Tim Edwards, 5/16/11, for Open Circuit Design
# Modified April 2013 for use with qflow
@@ -12,7 +10,7 @@
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: placement.sh <project_path> <source_name>
+ echo Usage: graywolf.sh <project_path> <source_name>
exit 1
endif
@@ -25,7 +23,7 @@ if ($argc == 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: placement.sh [options] <project_path> <source_name>
+ echo Usage: graywolf.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -119,6 +117,40 @@ foreach f (${spicefile})
set spicepath="${spicepath} $p"
end
+# Add hard macros to spice path
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ # Too bad SPICE doesn't have an agreed-upon extension. Common ones are:
+ if ( ${file:e} == "sp" || ${file:e} == "spc" || \
+ ${file:e} == "spice" || ${file:e} == "cdl" || \
+ ${file:e} == "ckt" || ${file:e} == "net") then
+ set spicepath="${spicepath} -l ${sourcedir}/${macro_path}/${file}"
+ break
+ endif
+ end
+ end
+endif
+
+# Prepend techdir to each libertyfile unless libertyfile begins with "/"
+set abspath=`echo ${libertyfile} | cut -c1`
+if ( "${abspath}" == "/" ) then
+ set libertypath=${libertyfile}
+else
+ set libertypath=${techdir}/${libertyfile}
+endif
+
+if (! ${?qrouter_nocleanup} ) then
+ set qrouter_nocleanup = ""
+else
+ if (${qrouter_nocleanup} == 0) then
+ set qrouter_nocleanup = ""
+ else
+ set qrouter_nocleanup = "true"
+ endif
+endif
+
if (!($?logdir)) then
set logdir=${projectpath}/log
endif
@@ -192,8 +224,8 @@ ${bindir}/qrouter -i ${rootname}.info -c ${rootname}.cfg
# Spot check: Did qrouter produce file ${rootname}.info?
#---------------------------------------------------------------------
-if ( !( -f ${rootname}.info || \
- ( -f ${rootname}.info && -M ${rootname}.info < -M ${rootname}.pin ))) then
+if ( ! -f ${rootname}.info || \
+ ( -M ${rootname}.info < -M ${rootname}.pin )) then
echo "qrouter (-i) failure: No file ${rootname}.info." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -214,49 +246,60 @@ cd ${projectpath}
if ( "$techleffile" == "" ) then
set lefoptions=""
- set addsoptions=""
else
- set lefoptions="--lef ${techlefpath}"
- set addsoptions="-techlef ${techlefpath}"
+ set lefoptions="-l ${techlefpath}"
endif
-set lefoptions="${lefoptions} --lef ${lefpath}"
+set lefoptions="${lefoptions} -l ${lefpath}"
-# Pass additional .lef files to blif2cel.tcl from the hard macros list
+# Pass additional .lef files to vlog2Cel and DEF2Verilog from the hard macros list
if ( ${?hard_macros} ) then
foreach macro_path ( $hard_macros )
foreach file ( `ls ${sourcedir}/${macro_path}` )
if ( ${file:e} == "lef" ) then
- set lefoptions="${lefoptions} --hard-macro ${sourcedir}/${macro_path}/${file}"
- set addsoptions="${addsoptions} -hardlef ${sourcedir}/${macro_path}/${file}"
+ set lefoptions="${lefoptions} -l ${sourcedir}/${macro_path}/${file}"
endif
end
end
endif
-echo "Running blif2cel to generate input files for graywolf" |& tee -a ${synthlog}
-echo "blif2cel.tcl --blif ${synthdir}/${rootname}.blif ${lefoptions} --cel ${layoutdir}/${rootname}.cel" |& tee -a ${synthlog}
-${scriptdir}/blif2cel.tcl --blif ${synthdir}/${rootname}.blif \
- ${lefoptions} --cel ${layoutdir}/${rootname}.cel >>& ${synthlog}
+#------------------------------------------------------------------
+# Check for placement being re-run; in that case we want to restore
+# the pre-placement netlist.
+#------------------------------------------------------------------
+
+if ( -f ${synthdir}/${rootname}_synth.rtl.v && ( -M ${synthdir}/${rootname}_synth.rtl.v \
+ > -M ${synthdir}/${rootname}_sized.v ) ) then
+ echo "Restoring ${rootname}.rtl.v, ${rootname}.rtlnopwr.v, and ${rootname}.rtlbb.v from pre-placement backups" |& tee -a ${synthlog}
+ cp ${synthdir}/${rootname}_synth.rtl.v ${synthdir}/${rootname}.rtl.v
+ cp ${synthdir}/${rootname}_synth.rtlnopwr.v ${synthdir}/${rootname}.rtlnopwr.v
+ cp ${synthdir}/${rootname}_synth.rtlbb.v ${synthdir}/${rootname}.rtlbb.v
+endif
+
+echo "Running vlog2Cel to generate input files for graywolf" |& tee -a ${synthlog}
+echo "vlog2Cel ${lefoptions} -u $units -o ${layoutdir}/${rootname}.cel ${synthdir}/${rootname}.rtlnopwr.v" |& tee -a ${synthlog}
+
+${bindir}/vlog2Cel ${lefoptions} -u $units -o ${layoutdir}/${rootname}.cel \
+ ${synthdir}/${rootname}.rtlnopwr.v >>& ${synthlog}
set errcond = $status
if ( ${errcond} != 0 ) then
- echo "blif2cel.tcl failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "vlog2Cel failed with exit status ${errcond}" |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped on error condition." >>& ${synthlog}
exit 1
endif
#---------------------------------------------------------------------
-# Spot check: Did blif2cel produce file ${rootname}.cel?
+# Spot check: Did vlog2Cel produce file ${rootname}.cel?
#---------------------------------------------------------------------
-if ( !( -f ${layoutdir}/${rootname}.cel || \
- ( -f ${layoutdir}/${rootname}.cel && -M ${layoutdir}/${rootname}.cel \
- < -M ${rootname}.blif ))) then
- echo "blif2cel failure: No file ${rootname}.cel." |& tee -a ${synthlog}
- echo "blif2cel was called with arguments: ${synthdir}/${rootname}.blif "
- echo " ${lefpath} ${layoutdir}/${rootname}.cel"
+if ( ! -f ${layoutdir}/${rootname}.cel || \
+ ( -M ${layoutdir}/${rootname}.cel < -M ${rootname}.rtlnopwr.v )) then
+ echo "vlog2Cel failure: No file ${rootname}.cel." |& tee -a ${synthlog}
+ echo "vlog2Cel was called with arguments: ${lefpath} "
+ echo " -u $units -o ${layoutdir}/${rootname}.cel"
+ echo " ${synthdir}/${rootname}.rtlnopwr.v"
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
exit 1
@@ -310,52 +353,42 @@ endif
if ( ! ${?antennacell} ) then
set antennacell = ""
endif
+if ( ! ${?bodytiecell} ) then
+ set bodytiecell = ""
+endif
-if ("x$fillcell" != "x") then
- if ("x$decapcell" != "x") then
- if ("x$antennacell" != "x") then
- set fillers = "${fillcell},${decapcell},${antennacell}"
- else
- set fillers = "${fillcell},${decapcell},"
- endif
- else if ("x$antennacell" != "x") then
- set fillers = "${fillcell},,${antennacell}"
- else
- set fillers = "${fillcell}"
- endif
-else
- if ("x$decapcell" != "x") then
- if ("x$antennacell" != "x") then
- set fillers = ",${decapcell},${antennacell}"
- else
- set fillers = ",${decapcell},"
- endif
- set fillcell = "${decapcell}"
- else if ("x$antennacell" != "x") then
- set fillers = ",,${antennacell}"
- set fillcell = "${antennacell}"
- else
+set fillers = "${fillcell},${decapcell},${antennacell},${bodytiecell}"
+
+# For tools that only require one fill cell as option, make sure that
+# there is a valid fill cell type
+if ("x$fillcell" == "x") then
+ set fillcell = $decapcell
+endif
+if ("x$fillcell" == "x") then
+ set fillcell = $antennacell
+endif
+if ("x$fillcell" == "x") then
+ set fillcell = $bodytiecell
+endif
+if ("x$fillcell" == "x") then
# There is no fill cell, which is likely to produce poor results.
echo "Warning: No fill cell types are defined in the tech setup script."
echo "This is likely to produce poor layout and/or poor routing results."
- set fillers = ""
- set fillcell = ""
- endif
endif
if ( ${?initial_density} ) then
echo "Running decongest to set initial density of ${initial_density}" \
|& tee -a ${synthlog}
if ( ${?fill_ratios} ) then
- echo "decongest.tcl ${rootname} ${lefpath} ${fillers} ${initial_density} ${fill_ratios} --units=${units}" \
+ echo "decongest.tcl ${rootname} ${lefpath} ${fillers} ${initial_density} ${fill_ratios} --units=${units} --lib=${libertypath}" \
|& tee -a ${synthlog}
${scriptdir}/decongest.tcl ${rootname} ${lefpath} \
- ${fillers} ${initial_density} ${fill_ratios} --units=${units} |& tee -a ${synthlog}
+ ${fillers} ${initial_density} ${fill_ratios} --units=${units} --lib=${libertypath} |& tee -a ${synthlog}
else
- echo "decongest.tcl ${rootname} ${lefpath} ${fillers} ${initial_density} --units=${units}" \
+ echo "decongest.tcl ${rootname} ${lefpath} ${fillers} ${initial_density} --units=${units} --lib=${libertypath}" \
|& tee -a ${synthlog}
${scriptdir}/decongest.tcl ${rootname} ${lefpath} \
- ${fillers} ${initial_density} --units=${units} |& tee -a ${synthlog}
+ ${fillers} ${initial_density} --units=${units} --lib=${libertypath} |& tee -a ${synthlog}
endif
set errcond = $status
if ( ${errcond} != 0 ) then
@@ -433,8 +466,8 @@ endif
# Spot check: Did GrayWolf produce file ${rootname}.pin?
#---------------------------------------------------------------------
-if ( !( -f ${rootname}.pin || \
- ( -f ${rootname}.pin && -M ${rootname}.pin < -M ${rootname}.cel ))) then
+if ( ! -f ${rootname}.pin || \
+ ( -M ${rootname}.pin < -M ${rootname}.cel )) then
echo "GrayWolf failure: No file ${rootname}.pin." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -469,6 +502,16 @@ endif
if ($makedef == 1) then
+ echo "Running getantennacell to determine cell to use for antenna anchors." \
+ |& tee -a ${synthlog}
+ echo "getantennacell.tcl $rootname ${lefpath} $antennacell" |& tee -a ${synthlog}
+ set useantennacell=`${scriptdir}/getantennacell.tcl $rootname \
+ ${lefpath} $antennacell | grep antenna= | cut -d= -f2 | cut -d/ -f1`
+
+ if ( "${useantennacell}" != "" ) then
+ echo "Using cell ${useantennacell} for antenna anchors" |& tee -a ${synthlog}
+ endif
+
# Run getfillcell to determine which cell should be used for fill to
# match the width specified for feedthroughs in the .par file. If
# nothing is returned by getfillcell, then either feedthroughs have
@@ -535,8 +578,8 @@ if ($makedef == 1) then
# Spot check: Did place2def produce file ${rootname}.def?
#---------------------------------------------------------------------
- if ( !( -f ${rootname}.def || \
- ( -f ${rootname}.def && -M ${rootname}.def < -M ${rootname}.pin ))) then
+ if ( ! -f ${rootname}.def || \
+ ( -M ${rootname}.def < -M ${rootname}.pin )) then
echo "place2def failure: No file ${rootname}.def." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -547,28 +590,30 @@ if ($makedef == 1) then
# Add spacer cells to create a straight border on the right side
#---------------------------------------------------------------------
- if ( !(${?nospacers}) && (-f ${scriptdir}/addspacers.tcl) ) then
+ if ( !(${?nospacers}) && (-f ${bindir}/addspacers) ) then
# Fill will use just the fillcell for padding under power buses
# and on the edges (to do: refine this to use other spacer types
# if the width options are more flexible).
if ( !( ${?addspacers_options} )) then
- set addspacers_options = "${addsoptions}"
- else
- set addspacers_options = "${addsoptions} ${addspacers_options}"
+ set addspacers_options = ""
endif
+ set addspacers_options = "${addspacers_options} -p $vddnet -g $gndnet -f ${fillcell} -O"
+
+ # Specify default power and ground names, if not present in the LEF file.
echo "Running addspacers to generate power stripes and align cell right edge" \
|& tee -a ${synthlog}
- echo "addspacers.tcl ${addspacers_options} ${rootname} ${lefpath} ${fillcell}" \
+ echo "addspacers ${addspacers_options} ${lefoptions} -o ${rootname}_filled.def ${rootname}" \
|& tee -a ${synthlog}
- ${scriptdir}/addspacers.tcl ${addspacers_options} \
- ${rootname} ${lefpath} ${fillcell} >>& ${synthlog}
+ rm -f ${rootname}_filled.def
+ ${bindir}/addspacers ${addspacers_options} ${lefoptions} \
+ -o ${rootname}_filled.def ${rootname} >>& ${synthlog}
set errcond = $status
if ( ${errcond} != 0 ) then
- echo "addspacers.tcl failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "addspacers failed with exit status ${errcond}" |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped on error condition." >>& ${synthlog}
exit 1
@@ -608,8 +653,8 @@ if ($makedef == 1) then
# Check if the _mod.def output file was generated, and if so, rename it
# back to plain .def.
- if ( !( -f ${rootname}_mod.def || ( -f ${rootname}_mode.def && \
- -M ${rootname}_mod.def < -M ${rootname}.def ))) then
+ if ( ! -f ${rootname}_mod.def || ( -M ${rootname}_mod.def \
+ < -M ${rootname}.def )) then
echo "Error (ignoring):"
echo " arrangepins.tcl failed to generate file ${rootname}_mod.def."
else
@@ -708,7 +753,7 @@ if ($makedef == 1) then
endif
# Add obstruction fence around design, created by place2def.tcl
- # and modified by addspacers.tcl
+ # and modified by addspacers
if ( -f ${rootname}.obs ) then
cat ${rootname}.obs >> ${rootname}.cfg
@@ -717,8 +762,8 @@ if ($makedef == 1) then
# Scripted version continues with the read-in of the DEF file
if (${scripting} == "T") then
- if ("x$antennacell" != "x") then
- echo "catch {qrouter::antenna init ${antennacell}}" >> ${rootname}.cfg
+ if ("x$useantennacell" != "x") then
+ echo "catch {qrouter::antenna init ${useantennacell}}" >> ${rootname}.cfg
endif
echo "read_def ${rootname}.def" >> ${rootname}.cfg
endif
@@ -733,7 +778,7 @@ if ($makedef == 1) then
cat ${rootname}.cfg2 >> ${rootname}.cfg
else
if (${scripting} == "T") then
- echo "qrouter::standard_route ${rootname}_route.def false" >> ${rootname}.cfg
+ echo "qrouter::standard_route ${rootname}_route.def false ${qrouter_nocleanup}" >> ${rootname}.cfg
# write_delays folded into standard_route in qrouter version 1.4.21.
if (${major} == 1 && ${minor} == 4 && ${subv} < 21) then
echo "qrouter::write_delays ${rootname}_route.rc" >> ${rootname}.cfg
@@ -747,30 +792,33 @@ if ($makedef == 1) then
#------------------------------------------------------------------
# Automatic optimization of buffer tree placement causes the
# original BLIF netlist, with tentative buffer assignments, to
- # be invalid. Use the blifanno.tcl script to back-annotate the
+ # be invalid. Use the DEF2Verilog tool to back-annotate the
# correct assignments into the original BLIF netlist, then
# use that BLIF netlist to regenerate the SPICE and RTL verilog
# netlists.
#------------------------------------------------------------------
- echo "blifanno.tcl ${synthdir}/${rootname}.blif ${rootname}.def ${synthdir}/${rootname}_anno.blif" \
+ echo "DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v -o ${synthdir}/${rootname}_anno.v" \
|& tee -a ${synthlog}
- ${scriptdir}/blifanno.tcl ${synthdir}/${rootname}.blif ${rootname}.def \
- ${synthdir}/${rootname}_anno.blif >>& ${synthlog}
+ echo "-p ${vddnet} -g ${gndnet} ${lefoptions} ${rootname}.def" |& tee -a ${synthlog}
+ ${bindir}/DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v \
+ -o ${synthdir}/${rootname}_anno.v \
+ -p ${vddnet} -g ${gndnet} \
+ ${lefoptions} ${rootname}.def >>& ${synthlog}
set errcond = $status
if ( ${errcond} != 0 ) then
- echo "blifanno.tcl failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "DEF2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped on error condition." >>& ${synthlog}
exit 1
endif
#------------------------------------------------------------------
- # Spot check: Did blifanno.tcl produce an output file?
+ # Spot check: Did DEF2Verilog produce an output file?
#------------------------------------------------------------------
- if ( !( -f ${synthdir}/${rootname}_anno.blif )) then
- echo "blifanno.tcl failure: No file ${rootname}_anno.blif." \
+ if ( !( -f ${synthdir}/${rootname}_anno.v )) then
+ echo "DEF2Verilog failure: No file ${rootname}_anno.v." \
|& tee -a ${synthlog}
echo "RTL verilog and SPICE netlists may be invalid if there" \
|& tee -a ${synthlog}
@@ -800,46 +848,81 @@ if ($makedef == 1) then
cp ${rootname}.rtlnopwr.v ${rootname}_synth.rtlnopwr.v
cp ${rootname}.rtlbb.v ${rootname}_synth.rtlbb.v
- echo "Running blif2Verilog." |& tee -a ${synthlog}
- ${bindir}/blif2Verilog -c -v ${vddnet} -g ${gndnet} \
- ${rootname}_anno.blif > ${rootname}.rtl.v
+ echo "Running vlog2Verilog." |& tee -a ${synthlog}
+ echo "vlog2Verilog -c -v ${vddnet} -g ${gndnet} -o ${rootname}.rtl.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtl.v ${rootname}_anno.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlnopwr.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlnopwr.v ${rootname}_anno.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlbb.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlbb.v ${rootname}_anno.v >>& ${synthlog}
- ${bindir}/blif2Verilog -c -p -v ${vddnet} -g ${gndnet} \
- ${rootname}_anno.blif > ${rootname}.rtlnopwr.v
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
- ${bindir}/blif2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} \
- ${rootname}_anno.blif > ${rootname}.rtlbb.v
+ echo "Running vlog2Spice." |& tee -a ${synthlog}
+ echo "vlog2Spice -i -l ${spicepath} -o ${rootname}.spc ${rootname}.rtl.v" \
+ |& tee -a ${synthlog}
+ ${bindir}/vlog2Spice -i -l ${spicepath} -o ${rootname}.spc ${rootname}.rtl.v >>& ${synthlog}
- echo "Running blif2BSpice." |& tee -a ${synthlog}
- ${bindir}/blif2BSpice -i -p ${vddnet} -g ${gndnet} -l \
- ${spicepath} ${rootname}_anno.blif \
- > ${rootname}.spc
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Spice failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
#------------------------------------------------------------------
- # Spot check: Did blif2Verilog or blif2BSpice exit with an error?
+ # Spot check: Did vlog2Verilog or vlog2Spice exit with an error?
#------------------------------------------------------------------
- if ( !( -f ${rootname}.rtl.v || ( -f ${rootname}.rtl.v && \
- -M ${rootname}.rtl.v < -M ${rootname}_anno.blif ))) then
- echo "blif2Verilog failure: No file ${rootname}.rtl.v created." \
+ if ( ! -f ${rootname}.rtl.v || ( -M ${rootname}.rtl.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtl.v created." \
|& tee -a ${synthlog}
endif
- if ( !( -f ${rootname}.rtlnopwr.v || ( -f ${rootname}.rtlnopwr.v && \
- -M ${rootname}.rtlnopwr.v < -M ${rootname}_anno.blif ))) then
- echo "blif2Verilog failure: No file ${rootname}.rtlnopwr.v created." \
+ if ( ! -f ${rootname}.rtlnopwr.v || ( -M ${rootname}.rtlnopwr.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlnopwr.v created." \
|& tee -a ${synthlog}
endif
- if ( !( -f ${rootname}.rtlbb.v || ( -f ${rootname}.rtlbb.v && \
- -M ${rootname}.rtlbb.v < -M ${rootname}_anno.blif ))) then
- echo "blif2Verilog failure: No file ${rootname}.rtlbb.v created." \
+ if ( ! -f ${rootname}.rtlbb.v || ( -M ${rootname}.rtlbb.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlbb.v created." \
|& tee -a ${synthlog}
endif
- if ( !( -f ${rootname}.spc || ( -f ${rootname}.spc && \
- -M ${rootname}.spc < -M ${rootname}_anno.blif ))) then
- echo "blif2BSpice failure: No file ${rootname}.spc created." \
+ if ( ! -f ${rootname}.spc || ( -M ${rootname}.spc \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Spice failure: No file ${rootname}.spc created." \
|& tee -a ${synthlog}
endif
diff --git a/scripts/migrate.sh b/scripts/magic_db.sh
index 79b74b5..3454e6a 100755
--- a/scripts/migrate.sh
+++ b/scripts/magic_db.sh
@@ -1,12 +1,12 @@
#!/usr/bin/tcsh -f
#----------------------------------------------------------
-# Layout and netlist migration script
+# Layout and netlist migration script using magic database
#----------------------------------------------------------
# Tim Edwards, 8/20/18, for Open Circuit Design
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: migrate.sh [options] <project_path> <source_name>
+ echo Usage: magic_db.sh [options] <project_path> <source_name>
exit 1
endif
@@ -20,7 +20,7 @@ if ($argc >= 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: migrate.sh [options] <project_path> <source_name>
+ echo Usage: magic_db.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -77,7 +77,7 @@ set lastlog=${logdir}/post_sta.log
if ( ${useexisting} == 1 ) then
set synthlog=${logdir}/lvs.log
else
- set synthlog=${logdir}/migrate.log
+ set synthlog=${logdir}/magic_db.log
rm -f ${logdir}/drc.log >& /dev/null
rm -f ${logdir}/lvs.log >& /dev/null
endif
@@ -248,13 +248,20 @@ echo "Running magic $version"
echo "magic -dnull -noconsole ${migrate_options} ${migratefile}" |& tee -a ${synthlog}
${bindir}/magic -dnull -noconsole ${migrate_options} ${migratefile} |& tee -a ${synthlog}
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "magic failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
#---------------------------------------------------------------------
# Spot check: Did the script generate .mag, .lef, and .spice files?
#---------------------------------------------------------------------
if ( ${useexisting} == 0 ) then
- if ( !( -f ${rootname}.mag || ( -f ${rootname}.mag && \
- -M ${rootname}.mag < -M ${rootname}.def ))) then
+ if ( ! -f ${rootname}.mag || ( -M ${rootname}.mag < -M ${rootname}.def )) then
echo "Migration failure: No .mag layout file generated." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
@@ -263,8 +270,7 @@ if ( ${useexisting} == 0 ) then
endif
endif
-if ( !( -f ${rootname}.lef || ( -f ${rootname}.lef && \
- -M ${rootname}.lef < -M ${rootname}.def ))) then
+if ( ! -f ${rootname}.lef || ( -M ${rootname}.lef < -M ${rootname}.def )) then
echo "Migration failure: No project .lef macro file generated." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
@@ -272,8 +278,7 @@ if ( !( -f ${rootname}.lef || ( -f ${rootname}.lef && \
exit 1
endif
-if ( !( -f ${rootname}.spice || ( -f ${rootname}.spice && \
- -M ${rootname}.spice < -M ${rootname}.def ))) then
+if ( ! -f ${rootname}.spice || ( -M ${rootname}.spice < -M ${rootname}.def )) then
echo "Migration failure: No project .spice extracted netlist generated." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
diff --git a/scripts/drc.sh b/scripts/magic_drc.sh
index 0ad344a..04e2fd2 100755
--- a/scripts/drc.sh
+++ b/scripts/magic_drc.sh
@@ -6,7 +6,7 @@
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: drc.sh [options] <project_path> <source_name>
+ echo Usage: magic_drc.sh [options] <project_path> <source_name>
exit 1
endif
@@ -21,7 +21,7 @@ if ($argc >= 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: drc.sh [options] <project_path> <source_name>
+ echo Usage: magic_drc.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -53,6 +53,10 @@ if (! ${?drc_options} ) then
set drc_options = ${options}
endif
+if (! ${?drc_gdsview} ) then
+ set drc_gdsview = ""
+endif
+
set rundrcfile="${layoutdir}/run_drc_${rootname}.tcl"
if (!($?logdir)) then
@@ -142,11 +146,11 @@ set subv=`echo $version | cut -d. -f3`
rm -f ${rundrcfile}
touch ${rundrcfile}
-if (! ($?gdsview)) then
- set gdsview=0
+if (! ($?drc_gdsview)) then
+ set drc_gdsview=0
endif
-if ( $gdsview == 1 ) then
+if ( $drc_gdsview == 1 ) then
cat >> ${rundrcfile} << EOF
gds readonly true
gds rescale false
@@ -184,6 +188,14 @@ echo "Running magic $version"
echo "magic -dnull -noconsole ${drc_options} ${rundrcfile}" |& tee -a ${synthlog}
${bindir}/magic -dnull -noconsole ${drc_options} ${rundrcfile} |& tee -a ${synthlog}
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "magic failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
#---------------------------------------------------------------------
# Spot check: Does the last line of the synthlog have "drc = 0"?
#---------------------------------------------------------------------
diff --git a/scripts/gdsii.sh b/scripts/magic_gds.sh
index 4c2ac41..5a1de27 100755
--- a/scripts/gdsii.sh
+++ b/scripts/magic_gds.sh
@@ -6,7 +6,7 @@
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: gdsii.sh [options] <project_path> <source_name>
+ echo Usage: magic_gds.sh [options] <project_path> <source_name>
exit 1
endif
@@ -21,7 +21,7 @@ if ($argc >= 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: gdsii.sh [options] <project_path> <source_name>
+ echo Usage: magic_gds.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -140,10 +140,19 @@ gds read $gfile
EOF
end
+# NOTE: "*hier write disable" and "*array write disable" prevent
+# magic from doing an exhaustive search on GDS layer interactions
+# between standard cells. This is disabled on the assumption that
+# the standard cells are properly designed and do not generate DRC
+# spacing errors when abutted. The standard cells will be abstract
+# views, anyway, so only a few layers like metal1 are represented.
+
cat >> ${gengdsfile} << EOF
load $rootname
select top cell
expand
+cif *hier write disable
+cif *array write disable
gds write $rootname
quit
EOF
@@ -156,12 +165,19 @@ echo "Running magic $version"
echo "magic -dnull -noconsole ${gdsii_options} ${gengdsfile}" |& tee -a ${synthlog}
${bindir}/magic -dnull -noconsole ${gdsii_options} ${gengdsfile} |& tee -a ${synthlog}
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "magic failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
#---------------------------------------------------------------------
# Spot check: Did magic produce file ${rootname}.gds?
#---------------------------------------------------------------------
-if ( !( -f ${rootname}.gds || ( -f ${rootname}.gds && -M ${rootname}.def \
- < -M ${rootname}.gds ))) then
+if ( ! -f ${rootname}.gds || ( -M ${rootname}.gds < -M ${rootname}.def )) then
echo "magic failure: No file ${rootname}.gds." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
diff --git a/scripts/display.sh b/scripts/magic_view.sh
index f3b397e..687d5ea 100755
--- a/scripts/display.sh
+++ b/scripts/magic_view.sh
@@ -1,12 +1,12 @@
#!/usr/bin/tcsh -f
#----------------------------------------------------------
-# Qflow layout display script using magic-8.0
+# Qflow layout display script using magic-8.X
#----------------------------------------------------------
# Tim Edwards, April 2013
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: display.sh [options] <project_path> <source_name>
+ echo Usage: magic_view.sh [options] <project_path> <source_name>
echo Options:
echo -g Use GDS view of standard cells (default auto-detect)
echo -l Use LEF view of standard cells
@@ -24,7 +24,7 @@ if ($argc == 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: display.sh [options] <project_path> <source_name>
+ echo Usage: magic_view.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
diff --git a/scripts/lvs.sh b/scripts/netgen_lvs.sh
index dadc591..b1976ab 100755
--- a/scripts/lvs.sh
+++ b/scripts/netgen_lvs.sh
@@ -1,12 +1,12 @@
#!/usr/bin/tcsh -f
#----------------------------------------------------------
-# LVS comparison script using magic
+# LVS comparison script using netgen
#----------------------------------------------------------
# Tim Edwards, 8/20/18, for Open Circuit Design
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: lvs.sh [options] <project_path> <source_name>
+ echo Usage: netgen_lvs.sh [options] <project_path> <source_name>
exit 1
endif
@@ -21,7 +21,7 @@ if ($argc >= 2) then
set argv1=`echo $cmdargs | cut -d' ' -f1`
set argv2=`echo $cmdargs | cut -d' ' -f2`
else
- echo Usage: lvs.sh [options] <project_path> <source_name>
+ echo Usage: netgen_lvs.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -80,16 +80,16 @@ endif
# Check if migration was run. Must have synthesis and layout extracted
# netlists. All netlists must be more recent than the project ".def" file.
-if ( ! ( -f ${synthdir}/${rootname}.spc || ( -f ${synthdir}/${rootname}.spc && \
- -M ${synthdir}/${rootname}.spc < -M ${synthdir}/${rootname}.blif ))) then
+if ( ! -f ${synthdir}/${rootname}.spc || \
+ ( -M ${synthdir}/${rootname}.spc < -M ${synthdir}/${rootname}.rtl.v )) then
echo "LVS failure: No schematic netlist found." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
exit 1
endif
-if ( ! ( -f ${layoutdir}/${rootname}.spice || ( -f ${layoutdir}/${rootname}.spice && \
- -M ${layoutdir}/${rootname}.spice < -M ${layoutdir}/${rootname}.def ))) then
+if ( ! -f ${layoutdir}/${rootname}.spice || \
+ ( -M ${layoutdir}/${rootname}.spice < -M ${layoutdir}/${rootname}.def )) then
echo "LVS failure: No layout extracted netlist found; migration was not run." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
@@ -150,19 +150,27 @@ cd ${layoutdir}
set outfile=comp.out
echo "Running netgen"
-echo 'netgen ${lvs_options} -batch lvs "${rootname}.spice ${rootname}" \
- "${synthdir}/${rootname}.spc ${rootname}" ${setup_script} ${outfile} \
- -json -blackbox' |& tee -a ${synthlog}
+echo netgen ${lvs_options} -batch lvs \"${rootname}.spice ${rootname}\" \
+ \"${synthdir}/${rootname}.spc ${rootname}\" ${setup_script} ${outfile} \
+ -json -blackbox |& tee -a ${synthlog}
${bindir}/netgen ${lvs_options} -batch lvs "${rootname}.spice ${rootname}" \
"${synthdir}/${rootname}.spc ${rootname}" ${setup_script} ${outfile} \
-json -blackbox |& tee -a ${synthlog}
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "netgen failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
#---------------------------------------------------------------------
# Spot check: Did netgen produce file comp.out?
#---------------------------------------------------------------------
-if ( !( -f comp.out || ( -f comp.out && -M comp.out < -M ${rootname}.spice ))) then
+if ( ! -f comp.out || ( -M comp.out < -M ${rootname}.spice )) then
echo "netgen failure: No file comp.out." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
diff --git a/scripts/opensta.sh b/scripts/opensta.sh
index 4ac20f7..162e790 100755
--- a/scripts/opensta.sh
+++ b/scripts/opensta.sh
@@ -143,7 +143,7 @@ endif
# Add hard macros
-hardmacrolibs = ""
+set hardmacrolibs=""
if ( ${?hard_macros} ) then
foreach macro_path ( $hard_macros )
foreach file ( `ls ${sourcedir}/${macro_path}` )
@@ -180,16 +180,20 @@ if ($dodelays == 1) then
# Run rc2dly
echo "Converting qrouter output to vesta delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -d ${rootname}.dly" \
+ echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.dly" |& tee -a ${synthlog}
${bindir}/rc2dly -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.dly
# Run rc2dly again to get SDF format file
echo "Converting qrouter output to SDF delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -d ${rootname}.sdf" \
+ echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.sdf" |& tee -a ${synthlog}
${bindir}/rc2dly -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.sdf
# Translate <, > to [ ] to match the verilog, as SDF format does not have
@@ -203,8 +207,8 @@ if ($dodelays == 1) then
cd ${synthdir}
# Spot check for output file
- if ( !( -f ${rootname}.sdf || \
- ( -M ${rootname}.sdf < -M ${layoutdir}/${rootname}.rc ))) then
+ if ( ! -f ${rootname}.sdf || \
+ ( -M ${rootname}.sdf < -M ${layoutdir}/${rootname}.rc )) then
echo "rc2dly failure: No file ${rootname}.sdf created." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
@@ -226,6 +230,18 @@ cd ${synthdir}
# Create a shell SDC file if one doesn't exist
# (This remains to be done properly and will probably need to be done by a script)
+if ($dodelays == 1) then
+
+if ( !(-f ${rootname}_post.sdc )) then
+ echo "Creating example SDC file for timing" |& tee -a ${synthlog}
+ cat > ${rootname}_post.sdc << EOF
+create_clock -name clock -period 20 [get_ports clock]
+set_propagated_clock [all_clocks]
+EOF
+endif
+
+else
+
if ( !(-f ${rootname}.sdc )) then
echo "Creating example SDC file for timing" |& tee -a ${synthlog}
cat > ${rootname}.sdc << EOF
@@ -233,6 +249,8 @@ create_clock -name clock -period 20 [get_ports clock]
EOF
endif
+endif
+
# Create the input script for OpenSTA
echo "Creating OpenSTA input file ${rootname}.conf" |& tee -a ${synthlog}
@@ -242,7 +260,7 @@ read_liberty -max ${libertymaxpath}
EOF
foreach libpath ( $hardmacrolibs )
- cat >> ${rootname}.conf << EOF
+cat >> ${rootname}.conf << EOF
read_celllib ${libpath}
EOF
end
@@ -258,8 +276,21 @@ read_sdf ${rootname}.sdf
EOF
endif
+if ($dodelays == 1) then
+
+cat >> ${rootname}.conf << EOF
+read_sdc ${rootname}_post.sdc
+EOF
+
+else
+
cat >> ${rootname}.conf << EOF
read_sdc ${rootname}.sdc
+EOF
+
+endif
+
+cat >> ${rootname}.conf << EOF
check_setup
report_annotated_check
report_annotated_delay
@@ -274,9 +305,9 @@ if ($dodelays == 1) then
else
echo "Running OpenSTA static timing analysis" |& tee -a ${synthlog}
endif
-echo "sta ${opensta_options} -f ${rootname}.conf" |& tee -a ${synthlog}
+echo "sta ${opensta_options} ${rootname}.conf" |& tee -a ${synthlog}
echo ""
-${bindir}/sta ${opensta_options} -f ${rootname}.conf |& tee -a ${synthlog}
+${bindir}/sta ${opensta_options} ${rootname}.conf |& tee -a ${synthlog}
echo ""
#------------------------------------------------------------
diff --git a/scripts/opentimer.sh b/scripts/opentimer.sh
index 16acad7..40236f6 100755
--- a/scripts/opentimer.sh
+++ b/scripts/opentimer.sh
@@ -143,7 +143,7 @@ endif
# Add hard macros
-hardmacrolibs = ""
+set hardmacrolibs = ""
if ( ${?hard_macros} ) then
foreach macro_path ( $hard_macros )
foreach file ( `ls ${sourcedir}/${macro_path}` )
@@ -180,16 +180,20 @@ if ($dodelays == 1) then
# Run rc2dly
echo "Converting qrouter output to vesta delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -d ${rootname}.dly" \
+ echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.dly" |& tee -a ${synthlog}
${bindir}/rc2dly -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.dly
# Run rc2dly again to get SPEF format file
echo "Converting qrouter output to SPEF delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -D : -r ${rootname}.rc -l ${libertypath} -d ${rootname}.spef" \
+ echo "Running rc2dly -D : -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.spef" |& tee -a ${synthlog}
${bindir}/rc2dly -D : -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.spef
# Translate <, >, and $ in file to _ to match the verilog.
@@ -204,8 +208,8 @@ if ($dodelays == 1) then
cd ${synthdir}
# Spot check for output file
- if ( !( -f ${rootname}.spef || \
- ( -M ${rootname}.spef < -M ${layoutdir}/${rootname}.rc ))) then
+ if ( ! -f ${rootname}.spef || \
+ ( -M ${rootname}.spef < -M ${layoutdir}/${rootname}.rc )) then
echo "rc2dly failure: No file ${rootname}.spef created." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
diff --git a/scripts/pinmanager.py.in b/scripts/pinmanager.py.in
index afde42c..42eff65 100755
--- a/scripts/pinmanager.py.in
+++ b/scripts/pinmanager.py.in
@@ -384,15 +384,11 @@ class PinManager(tkinter.Toplevel):
# To avoid allocation errors (X11 Pixmap, reason unknown)
# and to make things generally, cleaner, if the pin list
- # is longer than 12, then condense it.
+ # contains any vector longer than 12, then condense it.
- if len(newgroup['pins']) > 12:
- newgroup['pins'] = [newgroup['pins'][0] + '...' + newgroup['pins'][-1]]
- newgroup['fixed'] = [newgroup['fixed'][0]]
- newgroup['compressed'] = True
+ if len(newgroup['pins']) > 10:
+ newgroup = self.compress_group(newgroup)
- print('newgroup pins = ' + str(len(newgroup['pins'])))
- print('newgroup fixed = ' + str(len(newgroup['fixed'])))
# Redraw
self.populate()
return
@@ -410,7 +406,6 @@ class PinManager(tkinter.Toplevel):
newgroup = {}
newgroup['pins'] = []
newgroup['fixed'] = []
- newgroup['compressed'] = False
newgroup['name'] = group_name
newgroup['sides'] = 'LRTB'
newgroup['start'] = 0
@@ -549,34 +544,66 @@ class PinManager(tkinter.Toplevel):
# Make the pin draggable
self.add_draggable(pinframe, pinname)
- # Read a file of pads. Either cel2file or bliffile may be specified as the source.
+ # Read a file of pads. Either cel2file or fnetlist may be specified as the source.
# If both are specified, then cel2file takes precedence if both files exist.
- def readpads(self, cel2file, bliffile):
+ def readpads(self, cel2file, fnetlist):
# Save these references
self.cel2file = cel2file
- self.bliffile = bliffile
+ self.fnetlist = fnetlist
self.padgroups = []
- iorex = re.compile('^[ \t]*\.(?:inputs|outputs)[ \t]+(.+)$')
+ blifiorex = re.compile('^[ \t]*\.(?:inputs|outputs)[ \t]+(.+)$')
+ spciorex = re.compile('^\.subckt[ \t]+([^ \t]+)[ \t]+(.+)$', re.IGNORECASE)
grprex = re.compile('^[ \t]*padgroup[ \t]+([^ \t]+)[ \t]+([permute|nopermute]+)')
pinrex = re.compile('^[ \t]*twpin_([^ \t]+)[ \t]+([fixed|nonfixed]+)')
sidrex = re.compile('^[ \t]*restrict[ \t]+side[ \t]+([^ \t]+)')
spcrex = re.compile('^[ \t]*sidespace[ \t]+([^ \t]+)[ \t]+([^ \t]+)')
- # Always read the .blif file to get the full set of pins
+ # Read the verilog netlist to get the full set of pins.
+ # If the verilog netlist does not exist, see if there is a .blif file
+ # (this works for qflow-1.3 and earlier only)
+
fullpinlist = []
- if bliffile != None:
- if os.path.exists(bliffile):
- with open(bliffile, 'r') as ifile:
- for line in ifile:
- iomatch = iorex.match(line)
- if iomatch:
- iopins = iomatch.group(1).split()
- fullpinlist.extend(iopins)
+ if fnetlist != None:
+ if os.path.exists(fnetlist):
+ fileext = os.path.splitext(fnetlist)[1]
+ if fileext == '.blif':
+ with open(fnetlist, 'r') as ifile:
+ for line in ifile:
+ iomatch = blifiorex.match(line)
+ if iomatch:
+ iopins = iomatch.group(1).split()
+ fullpinlist.extend(iopins)
+ elif fileext == '.spc':
+ iopins = []
+ modname = os.path.splitext(os.path.split(fnetlist)[1])[0]
+ with open(fnetlist, 'r') as ifile:
+ nettext = ifile.read()
+ netlines = nettext.replace('\n+', ' ').splitlines()
+ for line in netlines:
+ iomatch = spciorex.match(line)
+ if iomatch:
+ subckt = iomatch.group(1)
+ if subckt == modname:
+ iopins = iomatch.group(2).split()
+ fpath = os.path.split(fnetlist)[0]
+ pgfile = os.path.join(fpath, modname + '_powerground')
+ if os.path.exists(pgfile):
+ with open(pgfile, 'r') as ifile:
+ for line in ifile:
+ pwrname = line.split('=')[1].strip().strip('"')
+ try:
+ iopins.remove(pwrname)
+ except:
+ print('Power pin ' + pwrname + ' is not in the pin list, ignoring.')
+ fullpinlist.extend(iopins)
+
+ elif fileext == '.v':
+ print("Don't know how to parse a verilog file. . . TBD. . . ")
else:
- print('Missing BLIF file name, cannot check for unassigned pins.', file=sys.stderr)
+ print('Missing BLIF or verilog netlist file name, cannot check for unassigned pins.', file=sys.stderr)
loaded = False
if cel2file != None:
@@ -605,7 +632,11 @@ class PinManager(tkinter.Toplevel):
else:
newgroup['pins'].append(pinname)
newgroup['fixed'].append(fixedtype)
- fullpinlist.remove(pinname)
+ try:
+ fullpinlist.remove(pinname)
+ except:
+ print('Error: Pin ' + pinname + ' is not in the netlist!',
+ file=sys.stderr)
if rmatch:
restrictside = rmatch.group(1)
if not newgroup:
@@ -626,14 +657,12 @@ class PinManager(tkinter.Toplevel):
# Compress long groups, mostly to avoid tkinter pixmap allocation
# error.
for group in self.padgroups:
- if len(group['pins']) > 12:
- group['pins'] = [group['pins'][0] + '...' + group['pins'][-1]]
- group['fixed'] = [group['fixed'][0]]
- group['compressed'] = True
+ if len(group['pins']) > 10:
+ group = self.compress_group(group)
if not loaded:
- if bliffile == None:
- print("No BLIF or .cel2 file, cannot continue with pin assignment!", file=sys.stderr)
+ if fnetlist == None:
+ print("No Verilog, BLIF, or .cel2 file, cannot continue with pin assignment!", file=sys.stderr)
return
# Convert any pins in the full pin list that were not in the .cel2 file
@@ -685,6 +714,120 @@ class PinManager(tkinter.Toplevel):
else:
group['fixed'].append('nonfixed')
+ # Compress long vectors with contiguous components in a group
+ def compress_group(self, pingroup):
+
+ # If pin group is already compressed, uncompress it first.
+ newpingroup = self.uncompress_group(pingroup)
+
+ # Accept array delimiters [..], <..>, and (..)
+ del1rex = re.compile('([^\[]+)([\[])([0-9]+)([\]])')
+ del2rex = re.compile('([^<]+)([<])([0-9]+)([>])')
+ del3rex = re.compile('([^\(]+)([\(])([0-9]+)([\)])')
+
+ compgroup = newpingroup.copy()
+ compgroup['pins'] = []
+ compgroup['fixed'] = []
+
+ currroot = ''
+ firstidx = lastidx = -1
+
+ for pin, fixed in zip(newpingroup['pins'], newpingroup['fixed']):
+ pmatch = del1rex.match(pin)
+ if not pmatch:
+ pmatch = del2rex.match(pin)
+ if not pmatch:
+ pmatch = del3rex.match(pin)
+
+ if pmatch:
+ pinroot = pmatch.group(1)
+ delim1 = pmatch.group(2)
+ pinidx = int(pmatch.group(3))
+ delim2 = pmatch.group(4)
+ else:
+ pinroot = pin
+ pinidx = -1
+
+ if pinroot == currroot:
+ if pinidx == (lastidx - 1) or pinidx == (lastidx + 1):
+ # Contiguous vector, so keep looking
+ lastidx = pinidx
+ else:
+ # Non-contiguous vector. Dump the previous part
+ if firstidx != lastidx:
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + ':' + str(lastidx) + delim2)
+ else:
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + delim2)
+ compgroup['fixed'].append(isfixed)
+ firstidx = lastidx = pinidx
+ else:
+ if firstidx != lastidx:
+ # Dump the compressed vector
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + ':' + str(lastidx) + delim2)
+ compgroup['fixed'].append(isfixed)
+ elif firstidx != -1:
+ # Dump the single vector component as-is
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + delim2)
+ compgroup['fixed'].append(isfixed)
+ if not pmatch:
+ # Not a vector, so just dump the signal as-is
+ compgroup['pins'].append(pin)
+ compgroup['fixed'].append(fixed)
+
+ # Record signal and look for more vector components
+ isfixed = fixed
+ currroot = pinroot
+ firstidx = lastidx = pinidx
+
+ # If there was a final vector group has not been output, do it now
+ if firstidx != lastidx:
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + ':' + str(lastidx) + delim2)
+ compgroup['fixed'].append(isfixed)
+ elif firstidx != -1:
+ compgroup['pins'].append(currroot + delim1 + str(firstidx) + delim2)
+ compgroup['fixed'].append(isfixed)
+
+ # Replace the group in self.padgroups
+ self.padgroups = [compgroup if item == pingroup else item for item in self.padgroups]
+
+ return compgroup
+
+ # Uncompress long vectors in a group that were compressed with compress_group()
+ # Note that unlike compress_group, the group in self.padgroups is not replaced.
+
+ def uncompress_group(self, pingroup):
+
+ # Accept array delimiters [..], <..>, and (..)
+ del1rex = re.compile('([^\[]+)([\[])([0-9]+):([0-9]+)([\]])')
+ del2rex = re.compile('([^<]+)([<])([0-9]+):([0-9]+)([>])')
+ del3rex = re.compile('([^\(]+)([\(])([0-9]+):([0-9]+)([\)])')
+
+ uncompgroup = pingroup.copy()
+ uncompgroup['pins'] = []
+ uncompgroup['fixed'] = []
+
+ for pin, fixed in zip(pingroup['pins'], pingroup['fixed']):
+ dmatch = del1rex.match(pin)
+ if not dmatch:
+ dmatch = del2rex.match(pin)
+ if not dmatch:
+ dmatch = del3rex.match(pin)
+
+ if dmatch:
+ rootname = dmatch.group(1)
+ delim1 = dmatch.group(2)
+ idx1 = int(dmatch.group(3))
+ idx2 = int(dmatch.group(4))
+ delim2 = dmatch.group(5)
+
+ for i in range(idx1,idx2+1):
+ uncompgroup['pins'].append(rootname + delim1 + str(i) + delim2)
+ uncompgroup['fixed'].append(fixed)
+ else:
+ uncompgroup['pins'].append(pin)
+ uncompgroup['fixed'].append(fixed)
+
+ return uncompgroup
# Write out the .cel2 file
def writepads(self, cel2file=None):
@@ -717,63 +860,18 @@ class PinManager(tkinter.Toplevel):
continue
print('Writing group ' + group['name'])
+ outgroup = self.uncompress_group(group)
# while writing output, ignore empty groups
- if group['compressed'] == True:
- del1rex = re.compile('([^<]+)[<]([0-9]+)[>]\.\.\.[^<]+[<]([0-9]+)[>]')
- del2rex = re.compile('([^\[]+)[\[]([0-9]+)[\]]\.\.\.[^\[]+[\[]([0-9]+)[\]]')
- del3rex = re.compile('([^\(]+)[\(]([0-9]+)[\)]\.\.\.[^\(]+[\(]([0-9]+)[\)]')
- pin = group['pins'][0]
- fixed = group['fixed'][0]
- dmatch1 = del1rex.match(pin)
- dmatch2 = del2rex.match(pin)
- dmatch3 = del3rex.match(pin)
- if dmatch1:
- rootname = dmatch1.group(1)
- startval = int(dmatch1.group(2))
- endval = int(dmatch1.group(3))
- ds = '<'
- de = '>'
- elif dmatch2:
- rootname = dmatch2.group(1)
- startval = int(dmatch2.group(2))
- endval = int(dmatch2.group(3))
- ds = '['
- de = ']'
- elif dmatch3:
- rootname = dmatch3.group(1)
- startval = int(dmatch3.group(2))
- endval = int(dmatch3.group(3))
- ds = '('
- de = ')'
- else:
- continue
-
- print('padgroup ' + group['name'] + ' ' + group['permute'], file=ofile)
- if startval < endval:
- for j in range(startval,endval + 1):
- pin = rootname + ds + str(j) + de
- print('twpin_' + pin + ' ' + fixed, file=ofile)
- else:
- for j in range(startval,endval - 1, -1):
- pin = rootname + ds + str(j) + de
- print('twpin_' + pin + ' ' + fixed, file=ofile)
- if group['sides'] != '':
- print('restrict side ' + group['sides'], file=ofile)
- if group['start'] != 0 or group['stop'] != 100:
- fstart = str(group['start'] / 100.0)
- fstop = str(group['stop'] / 100.0)
- print('sidespace ' + fstart + ' ' + fstop, file=ofile)
- print('', file=ofile)
- elif len(group['pins']) > 0:
- print('padgroup ' + group['name'] + ' ' + group['permute'], file=ofile)
- for pin, fixed in zip(group['pins'], group['fixed']):
+ if len(outgroup['pins']) > 0:
+ print('padgroup ' + outgroup['name'] + ' ' + outgroup['permute'], file=ofile)
+ for pin, fixed in zip(outgroup['pins'], outgroup['fixed']):
print('twpin_' + pin + ' ' + fixed, file=ofile)
- if group['sides'] != '':
- print('restrict side ' + group['sides'], file=ofile)
- if group['start'] != 0 or group['stop'] != 100:
- fstart = str(group['start'] / 100.0)
- fstop = str(group['stop'] / 100.0)
+ if outgroup['sides'] != '':
+ print('restrict side ' + outgroup['sides'], file=ofile)
+ if outgroup['start'] != 0 or outgroup['stop'] != 100:
+ fstart = str(outgroup['start'] / 100.0)
+ fstop = str(outgroup['stop'] / 100.0)
print('sidespace ' + fstart + ' ' + fstop, file=ofile)
print('', file=ofile)
diff --git a/scripts/place2def.tcl.in b/scripts/place2def.tcl.in
index bba26c5..af0128e 100755
--- a/scripts/place2def.tcl.in
+++ b/scripts/place2def.tcl.in
@@ -119,9 +119,8 @@ if [catch {open $obsname a} fobs] {
#-----------------------------------------------------------------
puts $fdef "VERSION 5.6 ;"
-puts $fdef "NAMESCASESENSITIVE ON ;"
puts $fdef "DIVIDERCHAR \"/\" ;"
-puts $fdef "BUSBITCHARS \"<>\" ;"
+puts $fdef "BUSBITCHARS \"\[\]\" ;"
puts $fdef "DESIGN $topname ;"
#-----------------------------------------------------------------
@@ -249,19 +248,19 @@ puts $fdef ""
# used by placement.
if {$metal1(orient) == "horizontal"} {
- set pitchx [expr $scale * $metal2(pitch)]
- set pitchy [expr $scale * $metal1(pitch)]
- set offsetx [expr $scale * $metal2(offset)]
- set offsety [expr $scale * $metal1(offset)]
- set widthx [expr $scale * $metal2(width)]
- set widthy [expr $scale * $metal1(width)]
+ set pitchx [expr round($scale * $metal2(pitch))]
+ set pitchy [expr round($scale * $metal1(pitch))]
+ set offsetx [expr round($scale * $metal2(offset))]
+ set offsety [expr round($scale * $metal1(offset))]
+ set widthx [expr round($scale * $metal2(width))]
+ set widthy [expr round($scale * $metal1(width))]
} else {
- set pitchx [expr $scale * $metal1(pitch)]
- set pitchy [expr $scale * $metal2(pitch)]
- set offsetx [expr $scale * $metal1(offset)]
- set offsety [expr $scale * $metal2(offset)]
- set widthx [expr $scale * $metal1(width)]
- set widthy [expr $scale * $metal2(width)]
+ set pitchx [expr round($scale * $metal1(pitch))]
+ set pitchy [expr round($scale * $metal2(pitch))]
+ set offsetx [expr round($scale * $metal1(offset))]
+ set offsety [expr round($scale * $metal2(offset))]
+ set widthx [expr round($scale * $metal1(width))]
+ set widthy [expr round($scale * $metal2(width))]
}
# Add numlayers to the configuration file now that we know it
@@ -334,10 +333,10 @@ set coreytop [expr $offsety + $coreytop - $coreybot]
set corexbot $offsetx
set coreybot $offsety
-set outcorextop [expr int($corextop * $multiplier)]
-set outcoreytop [expr int($coreytop * $multiplier)]
-set outcorexbot [expr int($corexbot * $multiplier)]
-set outcoreybot [expr int($coreybot * $multiplier)]
+set outcorextop [expr round($corextop * $multiplier)]
+set outcoreytop [expr round($coreytop * $multiplier)]
+set outcorexbot [expr round($corexbot * $multiplier)]
+set outcoreybot [expr round($coreybot * $multiplier)]
puts stdout "Core values: $corexbot $coreybot $corextop $coreytop"
puts stdout "Offsets: $offsetx $offsety"
@@ -359,10 +358,10 @@ set dieytop [expr $ytop - $cellybot + $halfpitchy]
set ygrid [expr floor($dieytop / $pitchy)]
set dieytop [expr $ygrid * $pitchy]
-set outdiexbot [expr int($diexbot * $multiplier)]
-set outdieybot [expr int($dieybot * $multiplier)]
-set outdiextop [expr int($diextop * $multiplier)]
-set outdieytop [expr int($dieytop * $multiplier)]
+set outdiexbot [expr round($diexbot * $multiplier)]
+set outdieybot [expr round($dieybot * $multiplier)]
+set outdiextop [expr round($diextop * $multiplier)]
+set outdieytop [expr round($dieytop * $multiplier)]
puts $fdef "DIEAREA ( $outdiexbot $outdieybot ) ( $outdiextop $outdieytop ) ;"
puts $fdef ""
@@ -377,7 +376,7 @@ set height [expr $dieytop - $dieybot]
for {set i 1} {$i <= $numlayers} {incr i} {
set mname [subst \$metal${i}(name)]
set mpitch [expr $scale * [subst \$metal${i}(pitch)]]
- set outmpitch [expr int($mpitch * $multiplier)]
+ set outmpitch [expr round($mpitch * $multiplier)]
if {[subst \$metal${i}(orient)] == "vertical"} {
set xtracks [expr 1 + int($width / $mpitch)];
puts $fdef "TRACKS X $outdiexbot DO $xtracks STEP $outmpitch LAYER $mname ;"
@@ -650,8 +649,8 @@ while {[gets $fpl1 line] >= 0} {
}
set posused(${llx},${lly}) $labname
- set outllx [expr int($llx * $multiplier)]
- set outlly [expr int($lly * $multiplier)]
+ set outllx [expr round($llx * $multiplier)]
+ set outlly [expr round($lly * $multiplier)]
puts $fdef "- $labname + NET $labname"
puts $fdef " + LAYER $labtype ( 0 0 ) ( 1 1 )"
@@ -669,7 +668,7 @@ while {[gets $fpl1 line] >= 0} {
# Pull out the fill cells if one or more was spliced into
# the component name
- if {[regexp {([^ \t]+)\.([^ \t.]+)} $instance lmatch rest baseinst] != 1} {
+ if {[regexp {([^ \t]+)\.([^ \t.:]+):(.+)} $instance lmatch rest baseinst thisinst] != 1} {
set baseinst $instance
}
set rest $instance
@@ -687,10 +686,10 @@ while {[gets $fpl1 line] >= 0} {
for {set j 0} {$j < $fmult} {incr j} {
set llxoff [expr $llx - $cellxbot]
- set outllxoff [expr int($llxoff * $multiplier)]
- set outllyoff [expr int($llyoff * $multiplier)]
+ set outllxoff [expr round($llxoff * $multiplier)]
+ set outllyoff [expr round($llyoff * $multiplier)]
incr numcomps -1
- set instname ${fillcell}_${i}_$baseinst
+ set instname ${fillcell}_${i}_$thisinst
puts $fdef "- $instname $fillcell + PLACED ( $outllxoff $outllyoff ) $ostr ;"
lappend fillinsts [list $instname $fillcell]
set llx [expr $llx + $fillwidth]
@@ -698,16 +697,16 @@ while {[gets $fpl1 line] >= 0} {
}
}
- # Get cellname from instance name.
- set instance $rest
- regsub {(.+)_[\d]+$} $instance {\1} cellname
+ # Get cellname and instance name from combined name generated by vlog2Cel.
+ # (instance name should be the same as thisinst, above)
+ regexp {(.+):(.+)$} $rest lmatch cellname instance
}
set llxoff [expr $llx - $cellxbot]
set llyoff [expr $lly - $cellybot]
- set outllxoff [expr int($llxoff * $multiplier)]
- set outllyoff [expr int($llyoff * $multiplier)]
+ set outllxoff [expr round($llxoff * $multiplier)]
+ set outllyoff [expr round($llyoff * $multiplier)]
puts $fdef "- $instance $cellname + PLACED ( $outllxoff $outllyoff ) $ostr ;"
@@ -801,7 +800,7 @@ while {[gets $fpin line] >= 0} {
# Each line in the file is:
# <netname> <subnet> <macro> <pinname> <x> <y> <row> <orient> <layer>
regexp {^([^ ]+)[ \t]+(\d+)[ \t]+([^ ]+)[ \t]+([^ ]+)[ \t]+([^ ]+)[ \t]+([^ ]+)[ \t]+[^ ]+[ \t]+[^ ]+[ \t]+([^ ]+)} \
- $line lmatch netname subnet instance pinname px py layer
+ $line lmatch netname subnet cellinst pinname px py layer
if {"$netname" != "$curnet"} {
set newnet 1
set curnet $netname
@@ -812,19 +811,19 @@ while {[gets $fpin line] >= 0} {
# Rip the filler cell name off the beginning of instances where
# they were spliced in
- while {[regexp {[^ \t.]+\.[0-9X]+\.([^ \t]+)} $instance lmatch rest] == 1} {
- set instance $rest
+ while {[regexp {[^ \t.]+\.[0-9X]+\.([^ \t]+)} $cellinst lmatch rest] == 1} {
+ set cellinst $rest
}
- if {([string first twfeed ${instance}] == -1) &&
+ if {([string first twfeed ${cellinst}] == -1) &&
([string first twfeed ${pinname}] != 0)} {
- if {[string first twpin_ ${instance}] == 0} {
+ if {[string first twpin_ ${cellinst}] == 0} {
if {$newnet == 0} {
puts $fdef "" ;# end each net component with newline
}
puts -nonewline $fdef " ( PIN ${pinname} ) "
set newnet 0
- } elseif {$instance != "PSEUDO_CELL"} {
+ } elseif {$cellinst != "PSEUDO_CELL"} {
# If pinname contains "bF$pin/", pin name follows this text
if {[regexp {.+bF\$pin/(.*)$} $pinname lmatch shortname]} {
@@ -833,11 +832,11 @@ while {[gets $fpin line] >= 0} {
# Avoid output for redundant entries generated by graywolf for
# pins it wants to route through both top and bottom.
- if {"${lastinst}" == "${instance}" && "${lastpin}" == "${pinname}"} {
+ if {"${lastinst}" == "${cellinst}" && "${lastpin}" == "${pinname}"} {
set skip 1
} else {
set skip 0
- set lastinst $instance
+ set lastinst $cellinst
set lastpin $pinname
}
@@ -846,12 +845,12 @@ while {[gets $fpin line] >= 0} {
}
# Avoid output for redundant entries generated by graywolf for
# pins it wants to route through both top and bottom.
+ regexp {(.+):(.+)$} $cellinst lmatch cellname instance
if {$skip == 0} {
puts -nonewline $fdef " ( ${instance} ${pinname} ) "
}
set newnet 0
- regexp {(.+)_[^_]+} $instance lmatch macro
- lappend maclist $macro
+ lappend maclist $cellname
}
}
}
diff --git a/scripts/place2def2.tcl.in b/scripts/place2def2.tcl.in
index dfa11f2..4761824 100755
--- a/scripts/place2def2.tcl.in
+++ b/scripts/place2def2.tcl.in
@@ -114,9 +114,8 @@ if [catch {open $cfgname w} fcfg] {
#-----------------------------------------------------------------
puts $fdef "VERSION 5.6 ;"
-puts $fdef "NAMESCASESENSITIVE ON ;"
puts $fdef "DIVIDERCHAR \"/\" ;"
-puts $fdef "BUSBITCHARS \"<>\" ;"
+puts $fdef "BUSBITCHARS \"\[\]\" ;"
puts $fdef "DESIGN $topname ;"
puts $fdef "UNITS DISTANCE MICRONS 100 ;"
puts $fdef ""
diff --git a/scripts/preproc.py.in b/scripts/preproc.py.in
new file mode 100755
index 0000000..f5cbd1f
--- /dev/null
+++ b/scripts/preproc.py.in
@@ -0,0 +1,452 @@
+#!ENV_PATH python3
+#--------------------------------------------------------------------
+#
+# preproc.py
+#
+# General purpose macro preprocessor
+#
+#--------------------------------------------------------------------
+# Usage:
+#
+# preproc.py input_file [output_file] [-D<variable> ...]
+#
+# Where <variable> may be a keyword or a key=value pair
+#
+# Syntax: Basically like cpp. However, this preprocessor handles
+# only a limited set of keywords, so it does not otherwise mangle
+# the file in the belief that it must be C code. Handling of boolean
+# relations is important, so these are thoroughly defined (see below)
+#
+# #if defined(<variable>) [...]
+# #ifdef <variable>
+# #ifndef <variable>
+# #elseif <variable>
+# #else
+# #endif
+#
+# #define <variable> [...]
+# #undef <variable>
+#
+# #include <filename>
+#
+# <variable> may be
+# <keyword>
+# <keyword>=<value>
+#
+# <keyword> without '=' is effectively the same as <keyword>=1
+# Lack of a keyword is equivalent to <keyword>=0, in a conditional.
+#
+# Boolean operators (in order of precedence):
+# ! NOT
+# && AND
+# || OR
+#
+# Comments:
+# Most comments (C-like or Tcl-like) are output as-is. A
+# line beginning with "###" is treated as a preprocessor
+# comment and is not copied to the output.
+#
+# Examples;
+# #if defined(X) || defined(Y)
+# #else
+# #if defined(Z)
+# #endif
+#--------------------------------------------------------------------
+
+import re
+import sys
+
+def solve_statement(condition):
+
+ defrex = re.compile('defined[ \t]*\(([^\)]+)\)')
+ orrex = re.compile('(.+)\|\|(.+)')
+ andrex = re.compile('(.+)&&(.+)')
+ notrex = re.compile('!([^&\|]+)')
+ parenrex = re.compile('\(([^\)]+)\)')
+ leadspacerex = re.compile('^[ \t]+(.*)')
+ endspacerex = re.compile('(.*)[ \t]+$')
+
+ matchfound = True
+ while matchfound:
+ matchfound = False
+
+ # Search for defined(K) (K must be a single keyword)
+ # If the keyword was defined, then it should have been replaced by 1
+ lmatch = defrex.search(condition)
+ if lmatch:
+ key = lmatch.group(1)
+ if key == 1 or key == '1' or key == True:
+ repl = 1
+ else:
+ repl = 0
+
+ condition = defrex.sub(str(repl), condition)
+ matchfound = True
+
+ # Search for (X) recursively
+ lmatch = parenrex.search(condition)
+ if lmatch:
+ repl = solve_statement(lmatch.group(1))
+ condition = parenrex.sub(str(repl), condition)
+ matchfound = True
+
+ # Search for !X recursively
+ lmatch = notrex.search(condition)
+ if lmatch:
+ only = solve_statement(lmatch.group(1))
+ if only == '1':
+ repl = '0'
+ else:
+ repl = '1'
+ condition = notrex.sub(str(repl), condition)
+ matchfound = True
+
+ # Search for A&&B recursively
+ lmatch = andrex.search(condition)
+ if lmatch:
+ first = solve_statement(lmatch.group(1))
+ second = solve_statement(lmatch.group(2))
+ if first == '1' and second == '1':
+ repl = '1'
+ else:
+ repl = '0'
+ condition = andrex.sub(str(repl), condition)
+ matchfound = True
+
+ # Search for A||B recursively
+ lmatch = orrex.search(condition)
+ if lmatch:
+ first = solve_statement(lmatch.group(1))
+ second = solve_statement(lmatch.group(2))
+ if first == '1' or second == '1':
+ repl = '1'
+ else:
+ repl = '0'
+ condition = orrex.sub(str(repl), condition)
+ matchfound = True
+
+ # Remove whitespace
+ lmatch = leadspacerex.match(condition)
+ if lmatch:
+ condition = lmatch.group(1)
+ lmatch = endspacerex.match(condition)
+ if lmatch:
+ condition = lmatch.group(1)
+
+ return condition
+
+def solve_condition(condition, keys, defines, keyrex):
+ # Do definition replacement on the conditional
+ for keyword in keys:
+ condition = keyrex[keyword].sub(defines[keyword], condition)
+
+ value = solve_statement(condition)
+ if value == '1':
+ return 1
+ else:
+ return 0
+
+def runpp(keys, keyrex, defines, ccomm, incdirs, inputfile, ofile):
+
+ includerex = re.compile('^[ \t]*#include[ \t]+"*([^ \t\n\r"]+)')
+ definerex = re.compile('^[ \t]*#define[ \t]+([^ \t]+)[ \t]+(.+)')
+ defrex = re.compile('^[ \t]*#define[ \t]+([^ \t\n\r]+)')
+ undefrex = re.compile('^[ \t]*#undef[ \t]+([^ \t\n\r]+)')
+ ifdefrex = re.compile('^[ \t]*#ifdef[ \t]+(.+)')
+ ifndefrex = re.compile('^[ \t]*#ifndef[ \t]+(.+)')
+ ifrex = re.compile('^[ \t]*#if[ \t]+(.+)')
+ elseifrex = re.compile('^[ \t]*#elseif[ \t]+(.+)')
+ elserex = re.compile('^[ \t]*#else')
+ endifrex = re.compile('^[ \t]*#endif')
+ commentrex = re.compile('^###[^#]*$')
+ ccstartrex = re.compile('/\*') # C-style comment start
+ ccendrex = re.compile('\*/') # C-style comment end
+
+ badifrex = re.compile('^[ \t]*#if[ \t]*.*')
+ badelserex = re.compile('^[ \t]*#else[ \t]*.*')
+
+ # This code is not designed to operate on huge files. Neither is it designed to be
+ # efficient.
+
+ # ifblock state:
+ # -1 : not in an if/else block
+ # 0 : no condition satisfied yet
+ # 1 : condition satisfied
+ # 2 : condition was handled, waiting for endif
+
+ ifile = False
+ try:
+ ifile = open(inputfile, 'r')
+ except FileNotFoundError:
+ for dir in incdirs:
+ try:
+ ifile = open(dir + '/' + inputfile, 'r')
+ except FileNotFoundError:
+ pass
+ else:
+ break
+
+ if not ifile:
+ print("Error: Cannot open file " + inputfile + " for reading.\n")
+ return
+
+ ccblock = -1
+ ifblock = -1
+ ifstack = []
+ lineno = 0
+
+ filetext = ifile.readlines()
+ for line in filetext:
+ lineno += 1
+
+ # C-style comments override everything else
+ if ccomm:
+ if ccblock == -1:
+ pmatch = ccstartrex.search(line)
+ if pmatch:
+ ematch = ccendrex.search(line[pmatch.end(0):])
+ if ematch:
+ line = line[0:pmatch.start(0)] + line[ematch.end(0)+2:]
+ else:
+ line = line[0:pmatch.start(0)]
+ ccblock = 1
+ elif ccblock == 1:
+ ematch = ccendrex.search(line)
+ if ematch:
+ line = line[ematch.end(0)+2:]
+ ccblock = -1
+ else:
+ continue
+
+ # Ignore lines beginning with "###"
+ pmatch = commentrex.match(line)
+ if pmatch:
+ continue
+
+ # Handle include. Note that this code does not expect or
+ # handle 'if' blocks that cross file boundaries.
+ pmatch = includerex.match(line)
+ if pmatch:
+ inclfile = pmatch.group(1)
+ runpp(keys, keyrex, defines, ccomm, incdirs, inclfile, ofile)
+ continue
+
+ # Handle define (with value)
+ pmatch = definerex.match(line)
+ if pmatch:
+ condition = pmatch.group(1)
+ value = pmatch.group(2)
+ defines[condition] = value
+ keyrex[condition] = re.compile(condition)
+ if condition not in keys:
+ keys.append(condition)
+ continue
+
+ # Handle define (simple case, no value)
+ pmatch = defrex.match(line)
+ if pmatch:
+ condition = pmatch.group(1)
+ print("Defrex condition is " + condition)
+ defines[condition] = '1'
+ keyrex[condition] = re.compile(condition)
+ if condition not in keys:
+ keys.append(condition)
+ print("Defrex value is " + defines[condition])
+ continue
+
+ # Handle undef
+ pmatch = undefrex.match(line)
+ if pmatch:
+ condition = pmatch.group(1)
+ if condition in keys:
+ defines.pop(condition)
+ keyrex.pop(condition)
+ keys.remove(condition)
+ continue
+
+ # Handle ifdef
+ pmatch = ifdefrex.match(line)
+ if pmatch:
+ if ifblock != -1:
+ ifstack.append(ifblock)
+
+ if ifblock == 1 or ifblock == -1:
+ condition = pmatch.group(1)
+ ifblock = solve_condition(condition, keys, defines, keyrex)
+ else:
+ ifblock = 2
+ continue
+
+ # Handle ifndef
+ pmatch = ifndefrex.match(line)
+ if pmatch:
+ if ifblock != -1:
+ ifstack.append(ifblock)
+
+ if ifblock == 1 or ifblock == -1:
+ condition = pmatch.group(1)
+ ifblock = solve_condition(condition, keys, defines, keyrex)
+ ifblock = 1 if ifblock == 0 else 0
+ else:
+ ifblock = 2
+ continue
+
+ # Handle if
+ pmatch = ifrex.match(line)
+ if pmatch:
+ if ifblock != -1:
+ ifstack.append(ifblock)
+
+ if ifblock == 1 or ifblock == -1:
+ condition = pmatch.group(1)
+ ifblock = solve_condition(condition, keys, defines, keyrex)
+ else:
+ ifblock = 2
+ continue
+
+ # Handle elseif
+ pmatch = elseifrex.match(line)
+ if pmatch:
+ if ifblock == -1:
+ print("Error: #elseif without preceding #if at line " + str(lineno) + ".")
+ ifblock = 0
+
+ if ifblock == 1:
+ ifblock = 2
+ elif ifblock != 2:
+ condition = pmatch.group(1)
+ ifblock = solve_condition(condition, keys, defines, keyrex)
+ continue
+
+ # Handle else
+ pmatch = elserex.match(line)
+ if pmatch:
+ if ifblock == -1:
+ print("Error: #else without preceding #if at line " + str(lineno) + ".")
+ ifblock = 0
+
+ if ifblock == 1:
+ ifblock = 2
+ elif ifblock == 0:
+ ifblock = 1
+ continue
+
+ # Handle endif
+ pmatch = endifrex.match(line)
+ if pmatch:
+ if ifblock == -1:
+ print("Error: #endif outside of #if block at line " + str(lineno) + " (ignored)")
+ elif ifstack:
+ ifblock = ifstack.pop()
+ else:
+ ifblock = -1
+ continue
+
+ # Check for 'if' or 'else' that were not properly formed
+ pmatch = badifrex.match(line)
+ if pmatch:
+ print("Error: Badly formed #if statement at line " + str(lineno) + " (ignored)")
+ if ifblock != -1:
+ ifstack.append(ifblock)
+
+ if ifblock == 1 or ifblock == -1:
+ ifblock = 0
+ else:
+ ifblock = 2
+ continue
+
+ pmatch = badelserex.match(line)
+ if pmatch:
+ print("Error: Badly formed #else statement at line " + str(lineno) + " (ignored)")
+ ifblock = 2
+ continue
+
+ # Ignore all lines that are not satisfied by a conditional
+ if ifblock == 0 or ifblock == 2:
+ continue
+
+ # Now do definition replacement on what's left (if anything)
+ for keyword in keys:
+ line = keyrex[keyword].sub(defines[keyword], line)
+
+ # Output the line
+ print(line, file=ofile, end='')
+
+ if ifblock != -1 or ifstack != []:
+ print("Error: input file ended with an unterminated #if block.")
+
+ if ifile != sys.stdin:
+ ifile.close()
+ return
+
+def printusage(progname):
+ print('Usage: ' + progname + ' input_file [output_file] [-options]')
+ print(' Options are:')
+ print(' -help Print this help text.')
+ print(' -ccomm Remove C comments in /* ... */ delimiters.')
+ print(' -D<def> Define word <def> and set its value to 1.')
+ print(' -D<def>=<val> Define word <def> and set its value to <val>.')
+ print(' -I<dir> Add <dir> to search path for input files.')
+ return
+
+if __name__ == '__main__':
+
+ # Parse command line for options and arguments
+ options = []
+ arguments = []
+ for item in sys.argv[1:]:
+ if item.find('-', 0) == 0:
+ options.append(item)
+ else:
+ arguments.append(item)
+
+ if len(arguments) > 0:
+ inputfile = arguments[0]
+ if len(arguments) > 1:
+ outputfile = arguments[1]
+ else:
+ outputfile = []
+ else:
+ printusage(sys.argv[0])
+ sys.exit(0)
+
+ defines = {}
+ keyrex = {}
+ keys = []
+ incdirs = []
+ ccomm = False
+ for item in options:
+ result = item.split('=')
+ if result[0] == '-help':
+ printusage(sys.argv[0])
+ sys.exit(0)
+ elif result[0] == '-ccomm':
+ ccomm = True
+ elif result[0][0:2] == '-I':
+ incdirs.append(result[0][2:])
+ elif result[0][0:2] == '-D':
+ keyword = result[0][2:]
+ try:
+ value = result[1]
+ except:
+ value = '1'
+ defines[keyword] = value
+ keyrex[keyword] = re.compile(keyword)
+ keys.append(keyword)
+ else:
+ print('Bad option ' + item + ', options are -help, -ccomm, -D<def> -I<dir>\n')
+ sys.exit(1)
+
+ if outputfile:
+ ofile = open(outputfile, 'w')
+ else:
+ ofile = sys.stdout
+
+ if not ofile:
+ print("Error: Cannot open file " + output_file + " for writing.")
+ sys.exit(1)
+
+ runpp(keys, keyrex, defines, ccomm, incdirs, inputfile, ofile)
+ if ofile != sys.stdout:
+ ofile.close()
+ sys.exit(0)
diff --git a/scripts/qflow.sh.in b/scripts/qflow.sh.in
index e6026ae..9225263 100644
--- a/scripts/qflow.sh.in
+++ b/scripts/qflow.sh.in
@@ -29,9 +29,9 @@ endif
# by -p. If the environment variable does not exist, the project
# root directory is assumed to be the current working directory.
-set project=`printenv QFLOW_PROJECT_ROOT`
-if ( $project == "" ) then
- set project=`pwd`
+set projectpath=`printenv QFLOW_PROJECT_ROOT`
+if ( $projectpath == "" ) then
+ set projectpath=`pwd`
endif
set modulename=""
@@ -52,6 +52,7 @@ set dolvs=0
set dodrc=0
set dogdsii=0
set doclean=0
+set dopurge=0
set dodisplay=0
while ($#argv > 0)
@@ -100,9 +101,8 @@ while ($#argv > 0)
shift
breaksw
case sta:
- case vesta:
- case opentimer:
- case opensta:
+ case timing:
+ case statictiming:
set dosta=1
set actions=1
shift
@@ -182,6 +182,11 @@ while ($#argv > 0)
set actions=1
shift
breaksw
+ case purge:
+ set dopurge=1
+ set actions=1
+ shift
+ breaksw
case display:
set dodisplay=1
set actions=1
@@ -260,6 +265,7 @@ if ($dohelp == 1 || $#argv != 0) then
echo ""
echo "Options: -T, --tech <name> Use technology <name>"
echo " -p, --project <name> Project root directory is <name>"
+ echo " -v, --version Print qflow version and revision"
if ($dohelp == 1) then
exit 0
else
@@ -281,7 +287,7 @@ else
echo "Technology set to $tech"
endif
-source QFLOW_SCRIPT_DIR/checkdirs.sh ${tech} ${project}
+source QFLOW_SCRIPT_DIR/checkdirs.sh ${tech} ${projectpath}
if ($status == 1) then
echo "Failure to verify working directories."
echo "Check QFLOW_PROJECT_ROOT environment variable."
@@ -289,7 +295,7 @@ if ($status == 1) then
endif
# Use find, not ls, to avoid error output when no match is found.
-set nvsrc = `find ${sourcedir} -name \*.v -print | grep -v match | wc -l`
+set nvsrc = `find ${sourcedir} -name \*.v -print | grep -v _mapped.v | wc -l`
set nsvsrc = `find ${sourcedir} -name \*.sv -print | wc -l`
# Verilog file root name is the same as the module name, at least until
@@ -299,7 +305,7 @@ set nsvsrc = `find ${sourcedir} -name \*.sv -print | wc -l`
if ($modulename == "") then
if ($nvsrc == 1) then
- set vsource=`ls ${sourcedir}/*.v`
+ set vsource=`ls ${sourcedir}/*.v | grep -v _mapped.v`
set modulename=${vsource:r}
else if ($nsvsrc == 1) then
set vsource=`ls ${sourcedir}/*.sv`
@@ -326,7 +332,7 @@ if ($vsource == "") then
# If there is no file list, then search for modules with this name.
if ( ! ($?source_file_list) ) then
# Check all .v and .sv files for one with the specified module.
- foreach file ( `ls ${sourcedir}/*.v ${sourcedir}/*.sv` )
+ foreach file ( `ls ${sourcedir}/*.v ${sourcedir}/*.sv | grep -v _mapped.v` )
set mline = `cat $file | grep module | grep ${modulename} | wc -l`
if ( $mline > 0 ) then
set vsource = ${file}
@@ -341,6 +347,11 @@ if ($vsource == "") then
endif
endif
+# Replace home path in vsource with tilde escape if needed
+if ( ${vsource} != "" ) then
+ set vsource="`echo $vsource | sed -e 's,^${HOME},~,'`"
+endif
+
#------------------------------------------------------------------
# Source the technology initialization script
#------------------------------------------------------------------
@@ -358,9 +369,9 @@ endif
# have not been selected. Finally, source the script.
#------------------------------------------------------------------
-set varfile=${projectpath}/qflow_vars.sh
-set execfile=${projectpath}/qflow_exec.sh
-set userfile=${projectpath}/project_vars.sh
+set varfile="${projectpath}"/qflow_vars.sh
+set execfile="${projectpath}"/qflow_exec.sh
+set userfile="${projectpath}"/project_vars.sh
#------------------------------------------------------------------
# Check if a variables file exists. If so, note that we are
@@ -381,7 +392,7 @@ endif
echo "#\!/usr/bin/tcsh -f" > ${varfile}
echo "#-------------------------------------------" >> ${varfile}
-echo "# qflow variables for project ${project}" >> ${varfile}
+echo "# qflow variables for project ${projectpath}" >> ${varfile}
echo "#-------------------------------------------" >> ${varfile}
echo "" >> ${varfile}
@@ -399,10 +410,68 @@ echo "" >> ${varfile}
echo "#\!/usr/bin/tcsh -f" > ${execfile}
echo "#-------------------------------------------" >> ${execfile}
-echo "# qflow exec script for project ${project}" >> ${execfile}
+echo "# qflow exec script for project ${projectpath}" >> ${execfile}
echo "#-------------------------------------------" >> ${execfile}
echo "" >> ${execfile}
+#---------------------------------------------------------
+# Make sure that the flow tools are defined with defaults
+#---------------------------------------------------------
+
+if ( ! (${?synthesis_tool}) ) then
+ if ( HAVE_YOSYS ) then
+ set synthesis_tool=yosys
+ endif
+endif
+
+if ( ! (${?placement_tool}) ) then
+ if ( HAVE_GRAYWOLF ) then
+ set placement_tool=graywolf
+ else if ( HAVE_REPLACE ) then
+ set placement_tool=replace
+ endif
+endif
+
+if ( ! (${?sta_tool}) ) then
+ set sta_tool=vesta
+endif
+
+if ( ! (${?router_tool}) ) then
+ if ( HAVE_QROUTER ) then
+ set router_tool=qrouter
+ endif
+endif
+
+if ( ! (${?migrate_tool}) ) then
+ if ( HAVE_MAGIC ) then
+ set migrate_tool=magic_db
+ endif
+endif
+
+if ( ! (${?lvs_tool}) ) then
+ if ( HAVE_NETGEN ) then
+ set lvs_tool=netgen_lvs
+ endif
+endif
+
+if ( ! (${?drc_tool}) ) then
+ if ( HAVE_MAGIC ) then
+ set drc_tool=magic_drc
+ endif
+endif
+
+if ( ! (${?gds_tool}) ) then
+ if ( HAVE_MAGIC ) then
+ set gds_tool=magic_gds
+ endif
+endif
+
+if ( ! (${?display_tool}) ) then
+ if ( HAVE_MAGIC ) then
+ set display_tool=magic_view
+ endif
+endif
+
#-----------------------------------------------------
# The file "project_vars.sh" will ONLY be written if
# one does not already exist, and then it will be an
@@ -413,11 +482,9 @@ echo "" >> ${execfile}
if ( ! -f ${userfile} ) then
echo "#\!/usr/bin/tcsh -f" > ${userfile}
echo "#------------------------------------------------------------" >> ${userfile}
- echo "# project variables for project ${project}" >> ${userfile}
+ echo "# project variables for project ${projectpath}" >> ${userfile}
echo "#------------------------------------------------------------" >> ${userfile}
echo "" >> ${userfile}
- echo "# Synthesis command options:" >> ${userfile}
- echo "# -------------------------------------------" >> ${userfile}
#------------------------------------------------------------------
# Check for any option defaults set by the .sh file for each entry
@@ -430,6 +497,112 @@ if ( ! -f ${userfile} ) then
# are aware what they may be changing.
#------------------------------------------------------------------
+ echo "# Flow options:" >> ${userfile}
+ echo "# -------------------------------------------" >> ${userfile}
+
+ if ( ${?synthesis_tool} ) then
+ echo \# set synthesis_tool = ${synthesis_tool} >> ${userfile}
+ else
+ if ( HAVE_YOSYS ) then
+ echo \# set synthesis_tool = yosys >> ${userfile}
+ set synthesis_tool = yosys
+ else
+ echo \# set synthesis_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?placement_tool} ) then
+ echo \# set placement_tool = ${placement_tool} >> ${userfile}
+ else
+ if ( HAVE_GRAYWOLF ) then
+ echo \# set placement_tool = graywolf >> ${userfile}
+ set placement_tool = graywolf
+ else if ( HAVE_REPLACE ) then
+ echo \# set placement_tool = replace >> ${userfile}
+ set placement_tool = replace
+ else
+ echo \# set placement_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?sta_tool} ) then
+ echo \# set sta_tool = ${sta_tool} >> ${userfile}
+ else
+ echo \# set sta_tool = vesta >> ${userfile}
+ set sta_tool = vesta
+ endif
+
+ if ( ${?router_tool} ) then
+ echo \# set router_tool = ${router_tool} >> ${userfile}
+ else
+ if ( HAVE_QROUTER ) then
+ echo \# set router_tool = qrouter >> ${userfile}
+ set router_tool = qrouter
+ else
+ echo \# set router_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?migrate_tool} ) then
+ echo \# set migrate_tool = ${migrate_tool} >> ${userfile}
+ else
+ if ( HAVE_MAGIC ) then
+ echo \# set migrate_tool = magic_db >> ${userfile}
+ set migrate_tool = magic_db
+ else
+ echo \# set migrate_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?lvs_tool} ) then
+ echo \# set lvs_tool = ${lvs_tool} >> ${userfile}
+ else
+ if ( HAVE_NETGEN ) then
+ echo \# set lvs_tool = netgen_lvs >> ${userfile}
+ set lvs_tool = netgen_lvs
+ else
+ echo \# set lvs_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?drc_tool} ) then
+ echo \# set drc_tool = ${drc_tool} >> ${userfile}
+ else
+ if ( HAVE_MAGIC ) then
+ echo \# set drc_tool = magic_drc >> ${userfile}
+ set drc_tool = magic_drc
+ else
+ echo \# set drc_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?gds_tool} ) then
+ echo \# set gds_tool = ${gds_tool} >> ${userfile}
+ else
+ if ( HAVE_MAGIC ) then
+ echo \# set gds_tool = magic_gds >> ${userfile}
+ set gds_tool = magic_gds
+ else
+ echo \# set gds_tool = >> ${userfile}
+ endif
+ endif
+
+ if ( ${?display_tool} ) then
+ echo \# set display_tool = ${display_tool} >> ${userfile}
+ else
+ if ( HAVE_MAGIC ) then
+ echo \# set display_tool = magic_view >> ${userfile}
+ set display_tool = magic_view
+ else
+ echo \# set display_tool = >> ${userfile}
+ endif
+ endif
+
+ echo "" >> ${userfile}
+
+ echo "# Synthesis command options:" >> ${userfile}
+ echo "# -------------------------------------------" >> ${userfile}
+
if ( ${?hard_macros} ) then
echo \# set hard_macros = \"${hard_macros}\" >> ${userfile}
else
@@ -570,6 +743,16 @@ if ( ! -f ${userfile} ) then
echo \# set qrouter_options = >> ${userfile}
endif
+ if ( ${?qrouter_nocleanup} ) then
+ echo \# set qrouter_nocleanup = \"${qrouter_nocleanup}\" >> ${userfile}
+ else
+ echo \# set qrouter_nocleanup = >> ${userfile}
+ endif
+
+ echo "" >> ${userfile}
+ echo "# STA command options:" >> ${userfile}
+ echo "# -------------------------------------------" >> ${userfile}
+
echo ""
echo "" >> ${userfile}
echo \# Minimum period of the clock use \"--period value\" \(value in ps\) \
@@ -581,11 +764,6 @@ if ( ! -f ${userfile} ) then
else
echo \# set opensta_options = >> ${userfile}
endif
- if ( ${?run_opensta} ) then
- echo \# set run_opensta = \"${run_opensta}\" >> ${userfile}
- else
- echo \# set run_opensta = 1 >> ${userfile}
- endif
endif
if ( HAVE_OPENTIMER ) then
if ( ${?opentimer_options} ) then
@@ -593,22 +771,17 @@ if ( ! -f ${userfile} ) then
else
echo \# set opentimer_options = >> ${userfile}
endif
- if ( ${?run_opentimer} ) then
- echo \# set run_opentimer = \"${run_opentimer}\" >> ${userfile}
- else
- echo \# set run_opentimer = 1 >> ${userfile}
- endif
endif
if ( ${?vesta_options} ) then
echo \# set vesta_options = \"${vesta_options}\" >> ${userfile}
else
echo \# set vesta_options = >> ${userfile}
endif
- if ( ${?run_vesta} ) then
- echo \# set run_vesta = \"${run_vesta}\" >> ${userfile}
- else
- echo \# set run_vesta = 0 >> ${userfile}
- endif
+
+ echo "" >> ${userfile}
+ echo "# Other options:" >> ${userfile}
+ echo "# -------------------------------------------" >> ${userfile}
+
if ( ${?migrate_options} ) then
echo \# set migrate_options = \"${migrate_options}\" >> ${userfile}
else
@@ -620,6 +793,24 @@ if ( ! -f ${userfile} ) then
echo \# set lef_options = >> ${userfile}
endif
+ if ( ${?drc_gdsview} ) then
+ echo \# set drc_gdsview = \"${drc_gdsview}\" >> ${userfile}
+ else
+ echo \# set drc_gdsview = >> ${userfile}
+ endif
+
+ if ( ${?drc_options} ) then
+ echo \# set drc_options = \"${drc_options}\" >> ${userfile}
+ else
+ echo \# set drc_options = >> ${userfile}
+ endif
+
+ if ( ${?gds_options} ) then
+ echo \# set gds_options = \"${gds_options}\" >> ${userfile}
+ else
+ echo \# set gds_options = >> ${userfile}
+ endif
+
echo "" >> ${userfile}
echo "#------------------------------------------------------------" >> ${userfile}
echo "" >> ${userfile}
@@ -633,9 +824,9 @@ if ($dosynth == 0) then
echo -n "# " >> ${execfile}
endif
if ( ${vsource} == "" ) then
- echo "${scriptdir}/synthesize.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+ echo "${scriptdir}/${synthesis_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
else
- echo "${scriptdir}/synthesize.sh ${projectpath} ${modulename} ${vsource} || exit 1" >> ${execfile}
+ echo "${scriptdir}/${synthesis_tool}.sh ${projectpath} ${modulename} ${vsource} || exit 1" >> ${execfile}
endif
if ($doplace == 0) then
@@ -643,102 +834,63 @@ if ($doplace == 0) then
endif
# Use -d because the user may decide not to run fanout buffering,
# and the files generated by place2def.tcl are required for routing.
-echo "${scriptdir}/placement.sh -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${placement_tool}.sh -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
set first=""
-if (HAVE_OPENSTA) then
- if ($dosta == 0) then
- echo -n "# " >> ${execfile}
- else if ( ! (${?run_opensta} ) ) then
- echo -n "# " >> ${execfile}
- endif
- echo "${scriptdir}/opensta.sh ${first} ${projectpath} ${modulename} || exit 1" >> ${execfile}
- set first="-a"
-endif
-if (HAVE_OPENTIMER) then
- if ($dosta == 0) then
- echo -n "# " >> ${execfile}
- else if ( ! (${?run_opentimer} ) ) then
+if ($dosta == 0) then
echo -n "# " >> ${execfile}
- endif
- echo "${scriptdir}/opentimer.sh ${first} ${projectpath} ${modulename} || exit 1" >> ${execfile}
- set first="-a"
endif
-if ($dosta == 0) then
- echo -n "# " >> ${execfile}
-else
- if ( ${?run_opensta} || ${?run_opentimer} ) then
- if ( ! (${?run_vesta} ) ) then
- echo -n "# " >> ${execfile}
- endif
- endif
+echo "${scriptdir}/${sta_tool}.sh ${first} ${projectpath} ${modulename} || exit 1" >> ${execfile}
+set first="-a"
endif
-echo "${scriptdir}/vesta.sh ${first} ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ($doroute == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/router.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${router_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
set first=""
-if (HAVE_OPENSTA) then
- if ($dobackanno == 0) then
- echo -n "# " >> ${execfile}
- else if ( ! (${?run_opensta} ) ) then
- echo -n "# " >> ${execfile}
- endif
- echo "${scriptdir}/opensta.sh ${first} -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
- set first="-a"
-endif
-if (HAVE_OPENTIMER) then
- if ($dobackanno == 0) then
- echo -n "# " >> ${execfile}
- else if ( ! (${?run_opentimer} ) ) then
- echo -n "# " >> ${execfile}
- endif
- echo "${scriptdir}/opentimer.sh ${first} -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
- set first="-a"
-endif
if ($dobackanno == 0) then
echo -n "# " >> ${execfile}
-else
- if ( ${?run_opensta} || ${?run_opentimer} ) then
- if ( ! (${?run_vesta} ) ) then
- echo -n "# " >> ${execfile}
- endif
- endif
endif
-echo "${scriptdir}/vesta.sh ${first} -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${sta_tool}.sh ${first} -d ${projectpath} ${modulename} || exit 1" >> ${execfile}
+set first="-a"
+endif
if ($domigrate == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/migrate.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${migrate_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ($dodrc == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/drc.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${drc_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ($dolvs == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/lvs.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${lvs_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ($dogdsii == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/gdsii.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${gds_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ($doclean == 0) then
echo -n "# " >> ${execfile}
endif
echo "${scriptdir}/cleanup.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+if ($dopurge == 0) then
+ echo -n "# " >> ${execfile}
+endif
+echo "${scriptdir}/cleanup.sh -p ${projectpath} ${modulename} || exit 1" >> ${execfile}
+
if ($dodisplay == 0) then
echo -n "# " >> ${execfile}
endif
-echo "${scriptdir}/display.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
+echo "${scriptdir}/${display_tool}.sh ${projectpath} ${modulename} || exit 1" >> ${execfile}
if ( $actions == 0 ) then
echo "No actions specified on command line;"
@@ -748,6 +900,15 @@ endif
chmod u+x ${execfile}
+# Note: Support legacy behavior; ${magicrc} may be a full path or may be
+# relative to ${techdir}
+
+if ( -f ${techdir}/${magicrc} ) then
+ set magicrc=${techdir}/${magicrc}
+else if (!( -f ${magicrc} )) then
+ echo "Error: Technology file not in ${techdir}/${magicrc} or ${magicrc}!"
+endif
+
# Drop the magic startup file into the layout directory if it does not exist
# If it exists but the technology directory has a newer file, or if we are
# changing technologies, then copy the old file to a backup and create a new
@@ -755,16 +916,16 @@ chmod u+x ${execfile}
if (-d ${layoutdir}) then
if (!(-f ${layoutdir}/.magicrc)) then
- cp ${techdir}/${magicrc} ${layoutdir}/.magicrc
+ cp ${magicrc} ${layoutdir}/.magicrc
else if ( $newtech == 1 ) then
echo "Technology changed: Old .magicrc file moved to .magicrc.orig"
cp ${layoutdir}/.magicrc ${layoutdir}/.magicrc.orig
- cp ${techdir}/${magicrc} ${layoutdir}/.magicrc
- else if ( -M ${techdir}/${magicrc} > -M ${layoutdir}/.magicrc ) then
+ cp ${magicrc} ${layoutdir}/.magicrc
+ else if ( -M ${magicrc} > -M ${layoutdir}/.magicrc ) then
echo -n "Technology .magicrc file has been updated. "
echo "Old .magicrc file moved to .magicrc.orig"
cp ${layoutdir}/.magicrc ${layoutdir}/.magicrc.orig
- cp ${techdir}/${magicrc} ${layoutdir}/.magicrc
+ cp ${magicrc} ${layoutdir}/.magicrc
endif
endif
diff --git a/scripts/qflow_manager.py.in b/scripts/qflow_manager.py.in
index 6860871..5a910a5 100755
--- a/scripts/qflow_manager.py.in
+++ b/scripts/qflow_manager.py.in
@@ -12,6 +12,7 @@
# Version 1. August 8-17, 2017
# Version for qflow distribution: April 19-29, 2018
# Updated to use new migrate, DRC, LVS, and GDS scripts: August 20, 2018
+# Updated for qflow-1.4: December 26, 2018
#-------------------------------------------------------------------------
import io
@@ -110,7 +111,7 @@ class QflowManager(ttk.Frame):
# Read user preferences file, get default font size from it.
prefspath = os.path.expanduser(prefsfile)
if os.path.exists(prefspath):
- with open(prefspath, 'r') as f:
+ with open(prefspath, 'r', encoding='utf-8') as f:
self.prefs = json.load(f)
if 'fontsize' in self.prefs:
fontsize = self.prefs['fontsize']
@@ -330,6 +331,219 @@ class QflowManager(ttk.Frame):
window.module.entry.insert(0, '(none)')
window.module.entry.pack(side = 'left')
+ window.reset = ttk.Frame(window)
+ window.reset.pack(side = 'top', fill = 'x')
+ window.reset.title = ttk.Label(window.reset, style = 'normal.TLabel',
+ text = 'Reset config files:')
+ window.reset.title.pack(side = 'left')
+ self.reset = tkinter.IntVar(window.reset)
+ self.reset.set(0)
+ window.reset.check = ttk.Checkbutton(window.reset, variable = self.reset)
+ window.reset.check.pack(side = 'left', fill = 'x')
+
+ # Preparation: Set up selection for all synthesis flow tools
+ # Find tools available at compile time and present selection for each.
+ # Synthesis:
+ self.synthesis_tools = []
+ self.run_synthesis = tkinter.StringVar(window)
+ if HAVE_YOSYS:
+ self.synthesis_tools.append('yosys')
+ self.run_synthesis.set('yosys')
+
+ # Placement:
+ self.run_placement = tkinter.StringVar(window)
+ self.placement_tools = []
+ if HAVE_GRAYWOLF:
+ self.placement_tools.append('graywolf')
+ self.run_placement.set('graywolf')
+ if HAVE_REPLACE:
+ self.placement_tools.append('replace')
+ self.run_placement.set('replace')
+
+ # Static timing analysis:
+ self.run_sta = tkinter.StringVar(window)
+ self.sta_tools = []
+ self.run_sta.set('vesta')
+ self.sta_tools.append('vesta')
+ if HAVE_OPENTIMER:
+ self.sta_tools.append('opentimer')
+ self.run_sta.set('opentimer')
+ if HAVE_OPENSTA:
+ self.sta_tools.append('opensta')
+ self.run_sta.set('opensta')
+
+ # Routing:
+ self.router_tools = []
+ self.run_router = tkinter.StringVar(window)
+ if HAVE_QROUTER:
+ self.router_tools.append('qrouter')
+ self.run_router.set('qrouter')
+
+ # Migration:
+ self.migrate_tools = []
+ self.run_migrate = tkinter.StringVar(window)
+ if HAVE_MAGIC:
+ self.migrate_tools.append('magic_db')
+ self.run_migrate.set('magic_db')
+
+ # DRC:
+ self.drc_tools = []
+ self.run_drc = tkinter.StringVar(window)
+ if HAVE_MAGIC:
+ self.drc_tools.append('magic_drc')
+ self.run_drc.set('magic_drc')
+
+ # LVS:
+ self.lvs_tools = []
+ self.run_lvs = tkinter.StringVar(window)
+ if HAVE_NETGEN:
+ self.run_lvs.set('netgen_lvs')
+ self.lvs_tools.append('netgen_lvs')
+
+ # GDS generation:
+ self.gds_tools = []
+ self.run_gds = tkinter.StringVar(window)
+ if HAVE_MAGIC:
+ self.gds_tools.append('magic_gds')
+ self.run_gds.set('magic_gds')
+
+ # Layout display:
+ self.display_tools = []
+ self.run_display = tkinter.StringVar(window)
+ if HAVE_MAGIC:
+ self.display_tools.append('magic_view')
+ self.run_display.set('magic_view')
+
+ # Now present the tool selection checklist
+ window.toolsframe = ttk.Frame(window)
+ window.toolsframe.pack(side = 'top')
+
+ window.toolstitle = ttk.Label(window.toolsframe, style = 'title.TLabel',
+ text = 'Selection of synthesis flow tools:')
+ window.toolstitle.pack(side = 'top')
+
+ if len(self.synthesis_tools) > 1:
+ if 'yosys' in self.synthesis_tools:
+ window.useyosys = ttk.Frame(window)
+ window.useyosys.pack(side = 'top', fill = 'x')
+ window.useyosys.title = ttk.Label(window.useyosys, style = 'normal.TLabel',
+ text = 'Use yosys for synthesis:')
+ window.useyosys.title.pack(side = 'left')
+ window.useyosys.check = ttk.Radiobutton(window.useyosys, variable = self.run_synthesis, value = 'yosys')
+ window.useyosys.check.pack(side = 'left')
+
+ if len(self.placement_tools) > 1:
+ if 'graywolf' in self.placement_tools:
+ window.usegraywolf = ttk.Frame(window)
+ window.usegraywolf.pack(side = 'top', fill = 'x')
+ window.usegraywolf.title = ttk.Label(window.usegraywolf, style = 'normal.TLabel',
+ text = 'Use graywolf for placement:')
+ window.usegraywolf.title.pack(side = 'left')
+ window.usegraywolf.check = ttk.Radiobutton(window.usegraywolf, variable = self.run_placement, value = 'graywolf')
+ window.usegraywolf.check.pack(side = 'left')
+
+ if 'replace' in self.placement_tools:
+ window.usereplace = ttk.Frame(window)
+ window.usereplace.pack(side = 'top', fill = 'x')
+ window.usereplace.title = ttk.Label(window.usereplace, style = 'normal.TLabel',
+ text = 'Use RePlAce for placement:')
+ window.usereplace.title.pack(side = 'left')
+ window.usereplace.check = ttk.Radiobutton(window.usereplace, variable = self.run_placement, value='replace')
+ window.usereplace.check.pack(side = 'left')
+
+ if len(self.sta_tools) > 1:
+ if 'vesta' in self.sta_tools:
+ window.usevesta = ttk.Frame(window)
+ window.usevesta.pack(side = 'top', fill = 'x')
+ window.usevesta.title = ttk.Label(window.usevesta, style = 'normal.TLabel',
+ text = 'Use vesta for STA:')
+ window.usevesta.title.pack(side = 'left')
+ window.usevesta.check = ttk.Radiobutton(window.usevesta, variable = self.run_sta, value='vesta')
+ window.usevesta.check.pack(side = 'left')
+
+ if 'opensta' in self.sta_tools:
+ window.useopensta = ttk.Frame(window)
+ window.useopensta.pack(side = 'top', fill = 'x')
+ window.useopensta.title = ttk.Label(window.useopensta, style = 'normal.TLabel',
+ text = 'Use OpenSTA for STA:')
+ window.useopensta.title.pack(side = 'left')
+ self.run_opensta = tkinter.BooleanVar(window.useopensta)
+ window.useopensta.check = ttk.Radiobutton(window.useopensta, variable = self.run_sta, value='opensta')
+ window.useopensta.check.pack(side = 'left')
+
+ if 'opentimer' in self.sta_tools:
+ window.useopentimer = ttk.Frame(window)
+ window.useopentimer.pack(side = 'top', fill = 'x')
+ window.useopentimer.title = ttk.Label(window.useopentimer, style = 'normal.TLabel',
+ text = 'Use OpenTimer for STA:')
+ window.useopentimer.title.pack(side = 'left')
+ self.run_opentimer = tkinter.BooleanVar(window.useopentimer)
+ window.useopentimer.check = ttk.Radiobutton(window.useopentimer, variable = self.run_sta, value='opentimer')
+ window.useopentimer.check.pack(side = 'left')
+
+ if len(self.router_tools) > 1:
+ if 'qrouter' in router_tools:
+ window.useqrouter = ttk.Frame(window)
+ window.useqrouter.pack(side = 'top', fill = 'x')
+ window.useqrouter.title = ttk.Label(window.usegraywolf, style = 'normal.TLabel',
+ text = 'Use qrouter for routing:')
+ window.useqrouter.title.pack(side = 'left')
+ window.useqrouter.check = ttk.Radiobutton(window.useqrouter, variable = self.run_router, value='qrouter')
+ window.useqrouter.check.pack(side = 'left')
+
+ if len(self.migrate_tools) > 1:
+ if 'magic_db' in migrate_tools:
+ window.usemagicdb = ttk.Frame(window)
+ window.usemagicdb.pack(side = 'top', fill = 'x')
+ window.usemagicdb.title = ttk.Label(window.usemagicdb, style = 'normal.TLabel',
+ text = 'Use magic database for migration:')
+ window.usemagicdb.title.pack(side = 'left')
+ window.usemagicdb.check = ttk.Radiobutton(window.usemagicdb, variable = self.run_migrate, value='magic_db')
+ window.usemagicdb.check.pack(side = 'left')
+
+ if len(self.drc_tools) > 1:
+ if 'magic_drc' in drc_tools:
+ window.usemagicdrc = ttk.Frame(window)
+ window.usemagicdrc.pack(side = 'top', fill = 'x')
+ window.usemagicdrc.title = ttk.Label(window.usemagicdrc, style = 'normal.TLabel',
+ text = 'Use magic for DRC:')
+ window.usemagicdrc.title.pack(side = 'left')
+ window.usemagicdrc.check = ttk.Radiobutton(window.usemagicdrc, variable = self.run_drc, value = 'magic_drc')
+ window.usemagicdrc.check.pack(side = 'left')
+
+ if len(self.lvs_tools) > 1:
+ if 'netgen_lvs' in lvs_tools:
+ window.usenetgenlvs = ttk.Frame(window)
+ window.usenetgenlvs.pack(side = 'top', fill = 'x')
+ window.usenetgenlvs.title = ttk.Label(window.usenetgenlvs, style = 'normal.TLabel',
+ text = 'Use netgen for LVS:')
+ window.usenetgenlvs.title.pack(side = 'left')
+ window.usenetgenlvs.check = ttk.Radiobutton(window.usenetgenlvs, variable = self.run_lvs, value = 'netgen_lvs')
+ window.usenetgenlvs.check.pack(side = 'left')
+
+ if len(self.gds_tools) > 1:
+ if 'magic_gds' in gds_tools:
+ window.usemagicgds = ttk.Frame(window)
+ window.usemagicgds.pack(side = 'top', fill = 'x')
+ window.usemagicgds.title = ttk.Label(window.usemagicgds, style = 'normal.TLabel',
+ text = 'Use magic for GDS:')
+ window.usemagicgds.title.pack(side = 'left')
+ window.usemagicgds.check = ttk.Radiobutton(window.usemagicgds, variable = self.run_gds, value = 'magic_gds')
+ window.usemagicgds.check.pack(side = 'left')
+
+ if len(self.display_tools) > 1:
+ if 'magic_view' in display_tools:
+ window.usemagicview = ttk.Frame(window)
+ window.usemagicview.pack(side = 'top', fill = 'x')
+ window.usemagicview.title = ttk.Label(window.usemagicview, style = 'normal.TLabel',
+ text = 'Use magic for layout viewing:')
+ window.usemagicview.title.pack(side = 'left')
+ window.usemagicview.check = ttk.Radiobutton(window.usemagicview, variable = self.run_display, value = 'magic_view')
+ window.usemagicview.check.pack(side = 'left')
+
+ #---------------------------------------------------
+ # Handle stop behavior between synthesis flow steps
+
window.stopframe = ttk.Frame(window)
window.stopframe.pack(side = 'top')
@@ -457,50 +671,6 @@ class QflowManager(ttk.Frame):
window.title = ttk.Label(window, text = 'Static Timing Analysis Settings', style = 'title.TLabel')
window.title.pack(side = 'top')
- # STA tools available are set at compile time
- self.sta_tools = []
- self.sta_tools.append('vesta')
- if HAVE_OPENSTA:
- self.sta_tools.append('opensta')
- if HAVE_OPENTIMER:
- self.sta_tools.append('opentimer')
-
- self.run_vesta = tkinter.BooleanVar(window)
- self.run_opensta = tkinter.BooleanVar(window)
- self.run_opentimer = tkinter.BooleanVar(window)
- self.run_vesta.set(1)
- self.run_opensta.set(0)
- self.run_opentimer.set(0)
-
- if len(self.sta_tools) > 1:
- window.usevesta = ttk.Frame(window)
- window.usevesta.pack(side = 'top', fill = 'x')
- window.usevesta.title = ttk.Label(window.usevesta, style = 'normal.TLabel',
- text = 'Use vesta for STA:')
- window.usevesta.title.pack(side = 'left')
- window.usevesta.check = ttk.Checkbutton(window.usevesta, variable = self.run_vesta)
- window.usevesta.check.pack(side = 'left')
-
- if 'opensta' in self.sta_tools:
- window.useopensta = ttk.Frame(window)
- window.useopensta.pack(side = 'top', fill = 'x')
- window.useopensta.title = ttk.Label(window.useopensta, style = 'normal.TLabel',
- text = 'Use OpenSTA for STA:')
- window.useopensta.title.pack(side = 'left')
- self.run_opensta = tkinter.BooleanVar(window.useopensta)
- window.useopensta.check = ttk.Checkbutton(window.useopensta, variable = self.run_opensta)
- window.useopensta.check.pack(side = 'left')
-
- if 'opentimer' in self.sta_tools:
- window.useopentimer = ttk.Frame(window)
- window.useopentimer.pack(side = 'top', fill = 'x')
- window.useopentimer.title = ttk.Label(window.useopentimer, style = 'normal.TLabel',
- text = 'Use OpenTimer for STA:')
- window.useopentimer.title.pack(side = 'left')
- self.run_opentimer = tkinter.BooleanVar(window.useopentimer)
- window.useopentimer.check = ttk.Checkbutton(window.useopentimer, variable = self.run_opentimer)
- window.useopentimer.check.pack(side = 'left')
-
window.longformat = ttk.Frame(window)
window.longformat.pack(side = 'top', fill = 'x')
window.longformat.title = ttk.Label(window.longformat, style = 'normal.TLabel',
@@ -635,6 +805,17 @@ class QflowManager(ttk.Frame):
command = self.qflow_swap_drc_lvs)
window.swap.check.pack(side = 'left')
+ window.drcgdsview = ttk.Frame(window)
+ window.drcgdsview.pack(side = 'top', fill = 'x')
+ window.drcgdsview.title = ttk.Label(window.drcgdsview, style = 'normal.TLabel',
+ text = 'Use GDS view of standard cells:')
+ window.drcgdsview.title.pack(side = 'left')
+ self.drcgdsview = tkinter.BooleanVar(window.drcgdsview)
+ self.drcgdsview.set(0)
+
+ window.drcgdsview.check = ttk.Checkbutton(window.drcgdsview, variable = self.drcgdsview)
+ window.drcgdsview.check.pack(side = 'left')
+
window.stop = ttk.Frame(window)
window.stop.pack(side = 'top', fill = 'x')
window.stop.title = ttk.Label(window.stop, style = 'normal.TLabel',
@@ -674,6 +855,9 @@ class QflowManager(ttk.Frame):
self.gdsview = tkinter.BooleanVar(window.gdsview)
self.gdsview.set(0)
+ window.gdsview.check = ttk.Checkbutton(window.gdsview, variable = self.gdsview)
+ window.gdsview.check.pack(side = 'left')
+
window.stop = ttk.Frame(window)
window.stop.pack(side = 'top', fill = 'x')
window.stop.title = ttk.Label(window.stop, style = 'normal.TLabel',
@@ -684,9 +868,6 @@ class QflowManager(ttk.Frame):
window.stop.check = ttk.Checkbutton(window.stop, variable = self.gds_stop)
window.stop.check.pack(side = 'left')
- window.gdsview.check = ttk.Checkbutton(window.gdsview, variable = self.gdsview)
- window.gdsview.check.pack(side = 'left')
-
self.toppane.settings.test_settings = ttk.Frame(self.toppane.settings)
window = self.toppane.settings.test_settings
window.title = ttk.Label(window, text = 'Testbench Settings', style = 'title.TLabel')
@@ -991,16 +1172,17 @@ class QflowManager(ttk.Frame):
pane.add(self.botpane)
pane.paneconfig(self.toppane, stretch='first')
- def print(self, message, file=None):
+ def print(self, message, file=None, end='\n', flush=False):
# Local print routine.
if not file:
file = ConsoleText.StdoutRedirector(self.text_box)
if self.stdout:
- print(message, file=file)
+ print(message, file=file, end=end)
+ if flush:
+ self.stdout.flush()
else:
self.init_messages.append(message)
-
def text_to_console(self):
# Redirect stdout and stderr to the console as the last thing to do. . .
# Otherwise errors in the GUI get sucked into the void.
@@ -1020,10 +1202,10 @@ class QflowManager(ttk.Frame):
def runpinmanager(self):
sdir = self.synthdir
ldir = self.layoutdir
- bliffile = sdir + '/' + self.project + '.blif'
+ spcfile = sdir + '/' + self.project + '.spc'
cel2file = ldir + '/' + self.project + '.cel2'
- self.pinmanager.readpads(cel2file, bliffile)
+ self.pinmanager.readpads(cel2file, spcfile)
self.pinmanager.populate()
self.pinmanager.open()
@@ -1074,6 +1256,8 @@ class QflowManager(ttk.Frame):
self.textreport.display(fullname)
# Layout edit function (run magic interactively)
+ # NOTE: This routine needs to call the display script, and the display script
+ # needs to handle all of the options presented here. FIXME
def edit_layout(self):
@@ -1093,11 +1277,19 @@ class QflowManager(ttk.Frame):
self.print('Displaying the completed routed layout, abstract view')
laystate = 'route1'
elif status == 'lvs' or status == 'drc':
- self.print('Displaying the completed routed layout, abstract view')
- laystate = 'route2'
- elif status == 'gds' or status == 'clean' or status == 'done':
+ # Variable gdsview overrides state
+ if self.drcgdsview.get() == 1:
+ self.print('Displaying the completed routed layout, full view')
+ laystate = 'drcgds'
+ else:
+ self.print('Displaying the completed routed layout, abstract view')
+ laystate = 'route2'
+ elif status == 'gds':
self.print('Displaying the completed routed layout, full view')
laystate = 'gds'
+ elif status == 'clean' or status == 'done':
+ self.print('Displaying the completed routed layout, full view')
+ laystate = 'gds2'
# Pick up LEF files from the project .cfg file
leffiles = []
@@ -1106,7 +1298,7 @@ class QflowManager(ttk.Frame):
# Pick up GDS files from the technology .sh file
gdsfiles = []
- if laystate == 'gds':
+ if laystate == 'gds' or laystate == 'drcgds' or laystate == 'gds2':
gdsfiles = self.get_project_gdsfiles()
# Make a copy of the .magicrc file for startup with some
@@ -1132,7 +1324,7 @@ class QflowManager(ttk.Frame):
print('lef read ' + leffile, file=ofile)
for gdsfile in gdsfiles:
print('gds read ' + gdsfile, file=ofile)
- if laystate == 'gds':
+ if laystate == 'gds2':
print('load gds_top', file=ofile)
print('gds read ' + self.project, file=ofile)
else:
@@ -1507,17 +1699,17 @@ class QflowManager(ttk.Frame):
key = vmatch.group(1)
value = vmatch.group(2)
if key == 'projectpath':
- self.rootpath = value
+ self.rootpath = os.path.expanduser(value)
elif key == 'techdir':
- tdir = value
+ tdir = os.path.expanduser(value)
elif key == 'sourcedir':
- srcdir = value
+ srcdir = os.path.expanduser(value)
elif key == 'synthdir':
- sdir = value
+ sdir = os.path.expanduser(value)
elif key == 'layoutdir':
- ldir = value
+ ldir = os.path.expanduser(value)
elif key == 'logdir':
- logdir = value
+ logdir = os.path.expanduser(value)
elif key == 'techname':
pdkname = value
@@ -1549,7 +1741,9 @@ class QflowManager(ttk.Frame):
cmatch = cmdrex.match(line)
if cmatch:
command = cmatch.group(1)
- if 'synthesize.sh' in command:
+ # NOTE: Need to modify if another synthesis tool is used;
+ # there needs to be a more general method here. . .
+ if 'yosys.sh' in command:
module = cmatch.group(3)
self.module.set(module)
self.project = module
@@ -1639,20 +1833,20 @@ class QflowManager(ttk.Frame):
else:
self.status = 'synth'
- # Step three: Check for .blif file
+ # Step three: Check for .v file
self.print("Step three.")
- bliffile = sdir + '/' + self.project + '.blif'
- if os.path.exists(bliffile):
- statbuf = os.stat(bliffile)
+ vlogfile = sdir + '/' + self.project + '.v'
+ if os.path.exists(vlogfile):
+ statbuf = os.stat(vlogfile)
if statbuf.st_mtime >= stime:
self.status = 'place'
stime = statbuf.st_mtime
else:
- self.print('Status: .blif file predates verilog file.')
+ self.print('Status: structural verilog file predates functional verilog file.')
self.print('Status set to "synth"')
return
else:
- self.print('Status: no .blif file.')
+ self.print('Status: no .v file.')
self.print('Status set to "synth"')
return
@@ -1668,7 +1862,7 @@ class QflowManager(ttk.Frame):
# Did addspacers fail to create power stripes due to bad user input?
logfile = self.rootpath + 'log/place.log'
if os.path.exists(logfile):
- with open(logfile, 'r') as ifile:
+ with open(logfile, 'r', encoding='utf-8') as ifile:
for line in ifile:
if 'No room for stripes' in line:
self.print('Status: Log file reports power buses not placed.')
@@ -1676,7 +1870,7 @@ class QflowManager(ttk.Frame):
self.status = 'place:fail'
return
else:
- self.print('Status: _unroute.def file predates .blif file.')
+ self.print('Status: _unroute.def file predates .v file.')
self.print('Status set to "place"')
self.status = 'place'
return
@@ -1975,9 +2169,9 @@ class QflowManager(ttk.Frame):
# Step twelve: Check for any residual file not removed by the
# cleanup process.
self.print("Step twelve.")
- junkfile = srcdir + '/' + self.project + '_mapped.blif'
+ junkfile = srcdir + '/' + self.project + '_mapped.v'
if os.path.exists(junkfile):
- self.print('Status: _mapped.blif file still sitting around.')
+ self.print('Status: _mapped.v file still sitting around.')
self.print('Status set to "clean"')
return
else:
@@ -1991,6 +2185,17 @@ class QflowManager(ttk.Frame):
self.qflow_get_status()
self.gui_set_status()
+ def remove_project_vars(self):
+ varsfile = self.rootpath + '/project_vars.sh'
+ if os.path.exists(varsfile):
+ os.remove(varsfile)
+
+ def remove_par_file(self):
+ ldir = self.layoutdir
+ parfile = ldir + '/' + self.project + '.par'
+ if os.path.exists(parfile):
+ os.remove(parfile)
+
# Read an existing project_vars.sh file and seed the values in the GUI
# It is okay for project_vars.sh file not to exist (GUI values will
# retain defaults). If "remove" is true, remove the file after reading.
@@ -2008,9 +2213,18 @@ class QflowManager(ttk.Frame):
otimerex = re.compile('[ \t]*set opentimer_options =[ \t]*(.*)$')
showrex = re.compile('[ \t]*set route_show =[ \t]*(.*)$')
lyerrex = re.compile('[ \t]*set route_layers =[ \t]*(.*)$')
- runvstarex = re.compile('[ \t]*set run_vesta =[ \t]*(.*)$')
- runostarex = re.compile('[ \t]*set run_opensta =[ \t]*(.*)$')
- runotimerex = re.compile('[ \t]*set run_opentimer =[ \t]*(.*)$')
+
+ synthrex = re.compile('[ \t]*set synthesis_tool =[ \t]*(.*)$')
+ placerex = re.compile('[ \t]*set placement_tool =[ \t]*(.*)$')
+ starex = re.compile('[ \t]*set sta_tool =[ \t]*(.*)$')
+ routerex = re.compile('[ \t]*set router_tool =[ \t]*(.*)$')
+ migraterex = re.compile('[ \t]*set migrate_tool =[ \t]*(.*)$')
+ drcrex = re.compile('[ \t]*set drc_tool =[ \t]*(.*)$')
+ lvsrex = re.compile('[ \t]*set lvs_tool =[ \t]*(.*)$')
+ gdsrex = re.compile('[ \t]*set gds_tool =[ \t]*(.*)$')
+ disprex = re.compile('[ \t]*set display_tool =[ \t]*(.*)$')
+ gdsoptrex = re.compile('[ \t]*set gds_options =[ \t]*(.*)$')
+ drcgdsrex = re.compile('[ \t]*set drc_gdsview =[ \t]*(.*)$')
# Power stripe defaults may be overridden, but we need to
# grab the defaults from the file instead of setting
@@ -2033,6 +2247,8 @@ class QflowManager(ttk.Frame):
self.route_graphics.set(1)
self.purge.set(0)
self.route_layers.set('max')
+ self.gdsview.set(0)
+ self.drcgdsview.set(0)
for line in varslines:
dmatch = densrex.match(line)
@@ -2042,23 +2258,22 @@ class QflowManager(ttk.Frame):
if vmatch:
if vmatch.group(1) == '"--long"':
self.long_format.set(1)
- # Currently no options handled for OpenSTA or OpenTimer
- # other than use/don't-use flag
- rvmatch = runvstarex.match(line)
- if rvmatch:
- self.run_vesta.set(rvmatch.group(1))
- rosmatch = runostarex.match(line)
- if rosmatch:
- self.run_opensta.set(rosmatch.group(1))
- rotmatch = runotimerex.match(line)
- if rotmatch:
- self.run_opentimer.set(rotmatch.group(1))
smatch = showrex.match(line)
if smatch:
- if smatch.group(1) == 'false':
+ if smatch.group(1) == 'false' or smatch.group(1) == '0':
self.route_graphics.set(0)
+ gmatch = gdsoptrex.match(line)
+ if gmatch:
+ if gmatch.group(1) == '"-g"':
+ self.gdsview.set(1)
+
+ dmatch = drcgdsrex.match(line)
+ if dmatch:
+ if dmatch.group(1) == 'true' or dmatch.group(1) == '1':
+ self.drcgdsview.set(1)
+
lmatch = lyerrex.match(line)
if lmatch:
self.route_layers.set(lmatch.group(1))
@@ -2082,10 +2297,40 @@ class QflowManager(ttk.Frame):
self.power_space.set(0)
# Empty string for addspacers_options indicates option has been
- # turned off manually although the default setting is on.
+ # disabled manually although the default setting is on.
dmatch = pow2rex.match(line)
if dmatch:
self.power.set(0)
+
+ # Set of lines indicating which tool to run, when there are options
+ rmatch = synthrex.match(line)
+ if rmatch:
+ self.run_synthesis.set(rmatch.group(1))
+ rmatch = placerex.match(line)
+ if rmatch:
+ self.run_placement.set(rmatch.group(1))
+ rmatch = starex.match(line)
+ if rmatch:
+ self.run_sta.set(rmatch.group(1))
+ rmatch = routerex.match(line)
+ if rmatch:
+ self.run_router.set(rmatch.group(1))
+ rmatch = migraterex.match(line)
+ if rmatch:
+ self.run_migrate.set(rmatch.group(1))
+ rmatch = drcrex.match(line)
+ if rmatch:
+ self.run_drc.set(rmatch.group(1))
+ rmatch = lvsrex.match(line)
+ if rmatch:
+ self.run_lvs.set(rmatch.group(1))
+ rmatch = gdsrex.match(line)
+ if rmatch:
+ self.run_gds.set(rmatch.group(1))
+ rmatch = disprex.match(line)
+ if rmatch:
+ self.run_display.set(rmatch.group(1))
+
if remove:
os.remove(varsfile)
return True
@@ -2139,15 +2384,15 @@ class QflowManager(ttk.Frame):
except ValueError:
do_layers = False
- run_vesta = False
- if (self.run_vesta.get() == 1):
- run_vesta = True
- run_opensta = False
- if (self.run_opensta.get() == 1):
- run_opensta = True
- run_opentimer = False
- if (self.run_opentimer.get() == 1):
- run_opentimer = True
+ do_drcgdsview = False
+ if self.drcgdsview.get() == 1:
+ do_drcgdsview = True
+
+ do_gdsview = False
+ if self.gdsview.get() == 1:
+ do_gdsview = True
+
+ run_sta = self.run_sta.get()
commentrex = re.compile('^[ \t]*#')
densrex = re.compile('#?[ \t]*set initial_density =')
@@ -2157,9 +2402,18 @@ class QflowManager(ttk.Frame):
otimerex = re.compile('#?[ \t]*set opentimer_options =')
showrex = re.compile('#?[ \t]*set route_show =')
lyerrex = re.compile('#?[ \t]*set route_layers =')
- rvstarex = re.compile('#?[ \t]*set run_vesta =')
- rostarex = re.compile('#?[ \t]*set run_opensta =')
- rotimerex = re.compile('#?[ \t]*set run_opentimer =')
+ gdsrex = re.compile('#?[ \t]*set gds_options =')
+ drcrex = re.compile('#?[ \t]*set drc_gdsview =')
+
+ tsynthrex = re.compile('#?[ \t]*set synthesis_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tplacerex = re.compile('#?[ \t]*set placement_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tstarex = re.compile('#?[ \t]*set sta_tool[ \t]* =[ \t]*([^ \t]*)$')
+ trouterex = re.compile('#?[ \t]*set router_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tmigraterex = re.compile('#?[ \t]*set migrate_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tdrcrex = re.compile('#?[ \t]*set drc_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tlvsrex = re.compile('#?[ \t]*set lvs_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tgdsrex = re.compile('#?[ \t]*set gds_tool[ \t]*=[ \t]*([^ \t]*)$')
+ tdisprex = re.compile('#?[ \t]*set display_tool[ \t]*=[ \t]*([^ \t]*)$')
with open(pvars, 'r') as ifile:
vartext = ifile.read()
@@ -2168,6 +2422,7 @@ class QflowManager(ttk.Frame):
seenpwr = False
for line in varlines:
linesub = line
+ # self.print("Line in: " + linesub, file=sys.stdin)
is_comment = commentrex.match(line)
if densrex.match(line):
if do_density:
@@ -2192,6 +2447,18 @@ class QflowManager(ttk.Frame):
newlines.append(linesub)
linesub = 'set addspacers_options = ""'
+ if gdsrex.match(line):
+ if do_gdsview:
+ linesub = 'set gds_options = "-g"'
+ elif not is_comment:
+ linesub = '# set gds_options = "-g"'
+
+ if drcrex.match(line):
+ if do_drcgdsview:
+ linesub = 'set drc_gdsview = 1'
+ elif not is_comment:
+ linesub = '# set drc_gdsview = 0'
+
if vstarex.match(line):
if do_long:
linesub = 'set vesta_options = "--long"'
@@ -2206,24 +2473,6 @@ class QflowManager(ttk.Frame):
if not is_comment:
linesub = '# set opentimer_options = '
- if rvstarex.match(line):
- if run_vesta:
- linesub = '# set run_vesta = 1'
- elif not is_comment:
- linesub = 'set run_vesta = 0'
-
- if rostarex.match(line):
- if run_opensta:
- linesub = 'set run_opensta = 1'
- elif not is_comment:
- linesub = '# set run_opensta ='
-
- if rotimerex.match(line):
- if run_opentimer:
- linesub = 'set run_opentimer = 1'
- elif not is_comment:
- linesub = '# set run_opentimer ='
-
if showrex.match(line):
if do_graph:
linesub = 'set route_show = 1'
@@ -2236,7 +2485,91 @@ class QflowManager(ttk.Frame):
elif not is_comment:
linesub = '# set route_layers ='
+ # Force the setting of the flow tools to use
+
+ tmatch = tsynthrex.match(line)
+ if tmatch:
+ if self.run_synthesis.get():
+ linesub = 'set synthesis_tool = ' + self.run_synthesis.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set synthesis_tool = '
+
+ tmatch = tplacerex.match(line)
+ if tmatch:
+ if self.run_placement.get():
+ linesub = 'set placement_tool = ' + self.run_placement.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set placement_tool = '
+
+ tmatch = tstarex.match(line)
+ if tmatch:
+ if self.run_sta.get():
+ linesub = 'set sta_tool = ' + self.run_sta.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set sta_tool = '
+
+ tmatch = trouterex.match(line)
+ if tmatch:
+ if self.run_router.get():
+ linesub = 'set router_tool = ' + self.run_router.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set router_tool = '
+
+ tmatch = tmigraterex.match(line)
+ if tmatch:
+ if self.run_migrate.get():
+ linesub = 'set migrate_tool = ' + self.run_migrate.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set migrate_tool = '
+
+ tmatch = tdrcrex.match(line)
+ if tmatch:
+ if self.run_drc.get():
+ linesub = 'set drc_tool = ' + self.run_drc.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set drc_tool = '
+
+ tmatch = tlvsrex.match(line)
+ if tmatch:
+ if self.run_lvs.get():
+ linesub = 'set lvs_tool = ' + self.run_lvs.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set lvs_tool = '
+
+ tmatch = tgdsrex.match(line)
+ if tmatch:
+ if self.run_gds.get():
+ linesub = 'set gds_tool = ' + self.run_gds.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set gds_tool = '
+
+ tmatch = tdisprex.match(line)
+ if tmatch:
+ if self.run_display.get():
+ linesub = 'set display_tool = ' + self.run_display.get()
+ elif tmatch.group(1):
+ linesub = line
+ else:
+ linesub = '# set display_tool = '
+
newlines.append(linesub)
+ # self.print("Line out: " + linesub, file=sys.stdout)
with open(pvars, 'w') as ofile:
for line in newlines:
@@ -2471,8 +2804,14 @@ class QflowManager(ttk.Frame):
# Read file contents into GUI settings (if they exist)
# Remove the files in preparation for regeneration
- existing = self.read_project_vars(remove=True)
- self.read_par_file(remove=True)
+ resetstate = True if self.reset.get() == 1 else False
+ if resetstate:
+ self.remove_project_vars()
+ self.remove_par_file()
+ existing = False
+ else:
+ existing = self.read_project_vars(remove=True)
+ self.read_par_file(remove=True)
# Set the project name from the verilog module name
self.project = self.module.get()
@@ -2497,6 +2836,7 @@ class QflowManager(ttk.Frame):
# the flow manager
if not existing:
self.read_project_vars(remove=False)
+ self.read_par_file(remove=False)
# Pull foundry and node from tech directory/symbolic link
pdkdir = os.path.realpath(self.pdkdir)
@@ -2516,6 +2856,7 @@ class QflowManager(ttk.Frame):
else:
# If the process got this far, enable the next button and mark step as done.
+ self.reset.set(0)
self.status = 'synth'
self.gui_set_status()
return
@@ -2541,11 +2882,11 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.synth_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.synth_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow synthsize ' + self.project)
+ self.print('Running: qflow ' + self.project + ' synthesize')
self.update_idletasks()
failed = False
- self.qflowproc = subprocess.Popen(['qflow', 'synthesize', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'synthesize'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
synthdir = self.synthdir
@@ -2580,7 +2921,7 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.place_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.place_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow place ' + self.project)
+ self.print('Running: qflow ' + self.project + ' place')
self.update_idletasks()
failed = False
@@ -2597,7 +2938,7 @@ class QflowManager(ttk.Frame):
self.pinmanager.writepads(cel2file)
# Run the qflow placement script
- self.qflowproc = subprocess.Popen(['qflow', 'place', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'place'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = layoutdir + '/' + self.project + '.def'
@@ -2631,13 +2972,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.timing_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.timing_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow sta ' + self.project)
+ self.print('Running: qflow ' + self.project + ' sta')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'sta', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'sta'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.logdir + '/sta.log'
@@ -2673,7 +3014,7 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.route_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.route_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow route ' + self.project)
+ self.print('Running: qflow ' + self.project + ' route')
self.update_idletasks()
# Check for long format option and update project_vars.sh
@@ -2687,7 +3028,7 @@ class QflowManager(ttk.Frame):
if os.path.exists(unroute):
shutil.copy(unroute, filename)
- self.qflowproc = subprocess.Popen(['qflow', 'route', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'route'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
try:
@@ -2720,13 +3061,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.backanno_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.backanno_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow backanno ' + self.project)
+ self.print('Running: qflow ' + self.project + ' backanno')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'backanno', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'backanno'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.logdir + '/post_sta.log'
@@ -2762,13 +3103,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.migrate_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.migrate_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow migrate ' + self.project)
+ self.print('Running: qflow ' + self.project + ' migrate')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'migrate', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'migrate'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.layoutdir + '/' + self.project + '.spice'
@@ -2809,13 +3150,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.drc_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.drc_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow drc ' + self.project)
+ self.print('Running: qflow ' + self.project + ' drc')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'drc', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'drc'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.logdir + '/drc.log'
@@ -2856,13 +3197,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.lvs_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.lvs_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow lvs ' + self.project)
+ self.print('Running: qflow ' + self.project + ' lvs')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'lvs', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'lvs'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.logdir + '/lvs.log'
@@ -2903,13 +3244,13 @@ class QflowManager(ttk.Frame):
self.toppane.checklist.gds_result.configure(text="In progress", style='normal.TButton')
self.toppane.checklist.gds_run.configure(text="Stop", style='normal.TButton')
- self.print('Running: qflow gdsii ' + self.project)
+ self.print('Running: qflow ' + self.project + ' gdsii')
self.update_idletasks()
# Check for long format option and update project_vars.sh
self.update_project_vars()
- self.qflowproc = subprocess.Popen(['qflow', 'gdsii', self.project],
+ self.qflowproc = subprocess.Popen(['qflow', self.project, 'gdsii'],
stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
filename = self.logdir + '/gdsii.log'
@@ -3051,12 +3392,18 @@ class QflowManager(ttk.Frame):
if os.path.exists('project_vars.sh'):
os.remove('project_vars.sh')
+ # self.print('Diagnostic: Removing magic startup script.')
+ ldir = self.layoutdir
+ if os.path.exists(ldir + '/.magicrc'):
+ os.remove(ldir + '/.magicrc')
+
# Set the PDK directory path to match the PDK name
for testname, testdir in zip(self.allpdknodes, self.allpdkdirs):
if testname == newpdk:
self.pdkdir = testdir
self.purge.set(1)
+ self.reset.set(1)
self.qflow_cleanup()
self.qflow_get_status()
self.gui_set_status()
@@ -3080,83 +3427,14 @@ class QflowManager(ttk.Frame):
self.update_idletasks()
failed = False
- srcdir = self.sourcedir
- sdir = self.synthdir
- ldir = self.layoutdir
- logdir = self.logdir
-
- # Remove files in qflow
- rfiles = []
- rfiles.append(srcdir + '/' + self.project + '_mapped.blif')
- rfiles.append(srcdir + '/' + self.project + '_mapped_tmp.blif')
-
- rfiles.append(sdir + '/' + self.project + '_anno.blif')
- rfiles.append(sdir + '/' + self.project + '_bak.blif')
- rfiles.append(sdir + '/' + self.project + '_nofanout')
- rfiles.append(sdir + '/' + self.project + '_synth.rtl.v')
- rfiles.append(sdir + '/' + self.project + '_synth.rtlbb.v')
- rfiles.append(sdir + '/' + self.project + '_synth.rtlnopwr.v')
- rfiles.append(sdir + '/' + self.project + '.rtl.v')
- rfiles.append(sdir + '/' + self.project + '.rtlbb.v')
- rfiles.append(sdir + '/' + self.project + '.rtlnopwr.v')
- rfiles.append(sdir + '/tmp.blif')
-
- rfiles.append(ldir + '/' + self.project + '.info')
- rfiles.append(ldir + '/' + self.project + '.mtmp')
- rfiles.append(ldir + '/' + self.project + '.obs')
- rfiles.append(ldir + '/' + self.project + '.pin')
- rfiles.append(ldir + '/' + self.project + '.pl1')
- rfiles.append(ldir + '/' + self.project + '.pl2')
-
- # "purge" clears out all files except the minimum set needed to
- # rebuild the design.
if do_purge == 1:
- rfiles.append(srcdir + '/' + self.project + '.ys')
- rfiles.append(sdir + '/' + self.project + '.blif')
- rfiles.append(sdir + '/' + self.project + '.spc')
- rfiles.append(sdir + '/' + self.project + '.xspice')
- rfiles.append(sdir + '/' + self.project + '.rtl.v')
- rfiles.append(sdir + '/' + self.project + '.rtlbb.v')
- rfiles.append(sdir + '/' + self.project + '.rtlnopwr.v')
- rfiles.append(sdir + '/' + self.project + '_synth.rtl.v')
- rfiles.append(sdir + '/' + self.project + '_synth.rtlbb.v')
- rfiles.append(sdir + '/' + self.project + '_synth.rtlnopwr.v')
- rfiles.append(sdir + '/' + self.project + '.dly')
- rfiles.append(sdir + '/' + self.project + '.spef')
- rfiles.append(sdir + '/' + self.project + '.sdf')
- rfiles.append(sdir + '/' + self.project + '_powerground')
- rfiles.append(ldir + '/' + self.project + '.cel')
- rfiles.append(ldir + '/' + self.project + '.cfg')
- rfiles.append(ldir + '/' + self.project + '.def')
- rfiles.append(ldir + '/' + self.project + '.lef')
- rfiles.append(ldir + '/' + self.project + '.mag')
- rfiles.append(ldir + '/' + self.project + '.gds')
- rfiles.append(ldir + '/' + self.project + '.rc')
- rfiles.append(ldir + '/comp.out')
- rfiles.append(ldir + '/comp.json')
- rfiles.append(ldir + '/qflow.magicrc')
- rfiles.append(ldir + '/' + self.project + '_unroute.def')
- rfiles.append(logdir + '/drc.log')
- rfiles.append(logdir + '/lvs.log')
- rfiles.append(logdir + '/gdsii.log')
- rfiles.append(logdir + '/migrate.log')
- rfiles.append(logdir + '/place.log')
- rfiles.append(logdir + '/post_sta.log')
- rfiles.append(logdir + '/prep.log')
- rfiles.append(logdir + '/route.log')
- rfiles.append(logdir + '/sta.log')
- rfiles.append(logdir + '/synth.log')
-
- for rfile in rfiles:
- if os.path.exists(rfile):
- os.remove(rfile)
-
- # Remove .ext files in layout
- if os.path.isdir(ldir):
- mfiles = os.listdir(ldir)
- for file in mfiles:
- if os.path.splitext(file)[1] == '.ext':
- os.remove(ldir + '/' + file)
+ qcommand = 'purge'
+ else:
+ qcommand = 'clean'
+ cproc = subprocess.Popen(['qflow', self.project, qcommand],
+ stdin = subprocess.DEVNULL, stdout = subprocess.PIPE,
+ stderr = subprocess.PIPE, bufsize = 0, cwd = self.rootpath)
+ cprocpair = cproc.communicate(timeout=1)
if failed:
self.toppane.checklist.clean_result.configure(text="Fail", style='red.TButton')
@@ -3193,10 +3471,10 @@ class QflowManager(ttk.Frame):
failed = True
break
- # Was a .blif file created and put in the synthesis directory?
+ # Was a .v file created and put in the synthesis directory?
- if not os.path.exists(self.synthdir + '/' + self.project + '.blif'):
- self.print("Qflow synthesis failed to generate an output .blif file")
+ if not os.path.exists(self.synthdir + '/' + self.project + '.v'):
+ self.print("Qflow synthesis failed to generate an output .v file")
failed = True
if failed:
@@ -3218,7 +3496,7 @@ class QflowManager(ttk.Frame):
logfile = self.logdir + '/place.log'
if os.path.exists(logfile):
with open(logfile, 'r', encoding='utf-8') as ifile:
- for line in ifile:
+ for line in ifile.read().splitlines():
if 'Synthesis flow stopped' in line:
self.print("Place script failed with error condition (see log file).")
failed = True
@@ -3399,7 +3677,8 @@ class QflowManager(ttk.Frame):
# Check if log/lvs.log shows an error occurring
logfile = self.logdir + '/lvs.log'
if os.path.exists(logfile):
- with open(logfile, 'r', encoding='utf-8') as ifile:
+ # File may contain non-ASCII characters, so assume ISO-8859-1
+ with open(logfile, 'r', encoding='ISO-8859-1') as ifile:
for line in ifile:
if 'Synthesis flow stopped' in line:
self.print("LVS failed with error condition (see log file).")
@@ -3550,7 +3829,8 @@ class QflowManager(ttk.Frame):
self.print('No technology setup file found.')
return False
else:
- if not os.path.isdir(pdkdir):
+ realpdkdir = os.path.realpath(pdkdir)
+ if not os.path.isdir(realpdkdir):
self.print('Error: No tech directory ' + pdkdir + ' found.', file=sys.stderr)
return False
else:
@@ -3648,13 +3928,21 @@ class QflowManager(ttk.Frame):
os.chdir(rootpath)
else:
rootpath = os.getcwd()
-
+
# If rootpath is a verilog filename, then the project path
# is the current working directory by definition.
rootname = os.path.split(rootpath)[1]
project = None
+ # Check for the rather common case (for me, at least) of having
+ # started the qflow GUI in one of the project subdirectories.
+ if rootname == 'layout' or rootname == 'synthesis' or rootname == 'source':
+ if os.path.isdir(rootpath + '/../synthesis'):
+ os.chdir('..')
+ rootpath = os.getcwd()
+ rootname = os.path.split(rootpath)[1]
+
if project_name:
project = project_name
@@ -3763,15 +4051,20 @@ class QflowManager(ttk.Frame):
self.print("qflow forced stop, status " + str(qflow_status), file=sys.stderr)
else:
for line in output[0].splitlines():
- self.print(line.decode('utf-8'))
+ try:
+ self.print(line.decode('utf-8'))
+ except:
+ pass
for line in output[1].splitlines():
- self.print(line.decode('utf-8'), file=sys.stderr)
+ try:
+ self.print(line.decode('utf-8'), file=sys.stderr)
+ except:
+ pass
self.print("qflow exited with status " + str(qflow_status))
break
else:
- self.update_idletasks()
-
+ self.update()
sresult = select.select([self.qflowproc.stdout, self.qflowproc.stderr], [], [], 0)[0]
if not self.qflowproc.stdout in sresult and not self.qflowproc.stderr in sresult:
diff --git a/scripts/router.sh b/scripts/qrouter.sh
index 6fa5081..b320a1e 100755
--- a/scripts/router.sh
+++ b/scripts/qrouter.sh
@@ -7,7 +7,7 @@
#----------------------------------------------------------
if ($#argv < 2) then
- echo Usage: router.sh [options] <project_path> <source_name>
+ echo Usage: qrouter.sh [options] <project_path> <source_name>
exit 1
endif
@@ -29,7 +29,7 @@ if ($argc >= 2) then
endif
endif
else
- echo Usage: router.sh [options] <project_path> <source_name>
+ echo Usage: qrouter.sh [options] <project_path> <source_name>
echo where
echo <project_path> is the name of the project directory containing
echo a file called qflow_vars.sh.
@@ -62,6 +62,75 @@ if (-f ${synthdir}/${rootname}_powerground) then
source ${synthdir}/${rootname}_powerground
endif
+# Prepend techdir to each leffile unless leffile begins with "/"
+set lefpath=""
+foreach f (${leffile})
+ set abspath=`echo ${f} | cut -c1`
+ if ( "${abspath}" == "/" ) then
+ set p=${leffile}
+ else
+ set p=${techdir}/${leffile}
+ endif
+ set lefpath="${lefpath} $p"
+end
+
+# Prepend techdir to techleffile unless techleffile begins with "/"
+set abspath=`echo ${techleffile} | cut -c1`
+if ( "${abspath}" == "/" ) then
+ set techlefpath=${techleffile}
+else
+ set techlefpath=${techdir}/${techleffile}
+endif
+
+# Prepend techdir to each spicefile unless spicefile begins with "/"
+set spicepath=""
+foreach f (${spicefile})
+ set abspath=`echo ${f} | cut -c1`
+ if ( "${abspath}" == "/" ) then
+ set p=${spicefile}
+ else
+ set p=${techdir}/${spicefile}
+ endif
+ set spicepath="${spicepath} $p"
+end
+
+# Add hard macros to spice path
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ # Too bad SPICE doesn't have an agreed-upon extension. Common ones are:
+ if ( ${file:e} == "sp" || ${file:e} == "spc" || \
+ ${file:e} == "spice" || ${file:e} == "cdl" || \
+ ${file:e} == "ckt" || ${file:e} == "net") then
+ set spicepath="${spicepath} -l ${sourcedir}/${macro_path}/${file}"
+ break
+ endif
+ end
+ end
+endif
+
+# Prepare LEF file options to pass to DEF2Verilog (post-route)
+
+if ( "$techleffile" == "" ) then
+ set lefoptions=""
+else
+ set lefoptions="-l ${techlefpath}"
+endif
+set lefoptions="${lefoptions} -l ${lefpath}"
+
+# Pass additional .lef files to DEF2Verilog from the hard macros list
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ if ( ${file:e} == "lef" ) then
+ set lefoptions="${lefoptions} -l ${sourcedir}/${macro_path}/${file}"
+ endif
+ end
+ end
+endif
+
# "-nog" (no graphics) has no graphics or console. "-noc"
# (no console) has graphics but no console. Console must
# always be disabled or else the script cannot capture the
@@ -108,30 +177,6 @@ else
endif
endif
-# Prepend techdir to leffile unless leffile begins with "/"
-set lefpath=""
-foreach f (${leffile})
- set abspath=`echo ${f} | cut -c1`
- if ( "${abspath}" == "/" ) then
- set p=${leffile}
- else
- set p=${techdir}/${leffile}
- endif
- set lefpath="${lefpath} $p"
-end
-
-# Ditto for spicefile
-set spicepath=""
-foreach f (${spicefile})
- set abspath=`echo ${f} | cut -c1`
- if ( "${abspath}" == "/" ) then
- set p=${spicefile}
- else
- set p=${techdir}/${spicefile}
- endif
- set spicepath="${spicepath} $p"
-end
-
#----------------------------------------------------------
# Done with initialization
#----------------------------------------------------------
@@ -198,20 +243,28 @@ else
-e remaining:\ \[1-9\]0\\\?\$ -e \\\*\\\*\\\*
endif
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "qrouter failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
#---------------------------------------------------------------------
# Spot check: Did qrouter produce file ${rootname}_route.def?
#---------------------------------------------------------------------
-if ( !( -f ${rootname}_route.def || ( -f ${rootname}_route.def && -M ${rootname}_route.def \
- < -M ${rootname}.def ))) then
+if ( ! -f ${rootname}_route.def || ( -M ${rootname}_route.def \
+ < -M ${rootname}.def )) then
echo "qrouter failure: No output file ${rootname}_route.def." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
exit 1
endif
-if ( !( -f ${rootname}_route.rc || ( -f ${rootname}_route.rc && \
- -M ${rootname}_route.rc < -M ${rootname}.def ))) then
+if ( ! -f ${rootname}_route.rc || ( -M ${rootname}_route.rc \
+ < -M ${rootname}.def )) then
echo "qrouter failure: No delay file ${rootname}_route.rc." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -222,8 +275,7 @@ endif
# Spot check: Did qrouter produce file failed.out?
#---------------------------------------------------------------------
-if ( -f failed.out && ( -M failed.out \
- > -M ${rootname}.def )) then
+if ( -f failed.out && ( -M failed.out > -M ${rootname}.def )) then
echo "qrouter failure: Not all nets were routed." |& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -231,63 +283,143 @@ if ( -f failed.out && ( -M failed.out \
endif
#---------------------------------------------------------------------
-# If qrouter generated an "antenna.out" file, then use it to
-# annotate the verilog and spice netlists. If there is a file
-# "fillcells.txt" file, then add those to the netlists as well,
-# while ignoring any that already appeared in the antenna.out file.
+# Back-annotate everything including fill cells and added antenna cell
+# connections by regenerating the structural verilog netlists from the
+# post-layout DEF file.
#---------------------------------------------------------------------
-if (${scripting} == "T") then
- # If fill cells were documented in "fillcells.txt", append the file to
- # "antenna.out" before processing.
- if ( -f fillcells.txt && ( -M fillcells.txt > -M $rootname}.cel )) then
- if ( -f antenna.out && ( -M antenna.out > -M ${rootname}.cel )) then
- cat fillcells.txt >> antenna.out
- else
- cp fillcells.txt antenna.out
- endif
+echo "DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v -o ${synthdir}/${rootname}_postroute.v" \
+ |& tee -a ${synthlog}
+echo "-p ${vddnet} -g ${gndnet} ${lefoptions} ${rootname}_route.def" |& tee -a ${synthlog}
+${bindir}/DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v \
+ -o ${synthdir}/${rootname}_postroute.v \
+ -p ${vddnet} -g ${gndnet} \
+ ${lefoptions} ${rootname}_route.def >>& ${synthlog}
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "DEF2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
+#------------------------------------------------------------------
+# Spot check: Did DEF2Verilog produce an output file?
+#------------------------------------------------------------------
+
+if ( !( -f ${synthdir}/${rootname}_postroute.v )) then
+ echo "DEF2Verilog failure: No file ${rootname}_postroute.v." \
+ |& tee -a ${synthlog}
+ echo "RTL verilog and SPICE netlists may be invalid if there" \
+ |& tee -a ${synthlog}
+ echo "were antenna connections made by the router." |& tee -a ${synthlog}
+ echo "Synthesis flow continuing, condition not fatal." >> ${synthlog}
+else
+ echo "" >> ${synthlog}
+ echo "Generating RTL verilog and SPICE netlist file in directory" \
+ |& tee -a ${synthlog}
+ echo " ${synthdir}" |& tee -a ${synthlog}
+
+ cd ${synthdir}
+
+ # Run vlog2Verilog and vlog2Spice on annotated netlist to get annotated
+ # versions of the different verilog styles and the SPICE netlist.
+
+ echo "Running vlog2Verilog." |& tee -a ${synthlog}
+ echo "vlog2Verilog -c -v ${vddnet} -g ${gndnet} -o ${rootname}.rtl.anno.v ${rootname}_postroute.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtl.anno.v ${rootname}_postroute.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlnopwr.anno.v ${rootname}_postroute.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlnopwr.anno.v ${rootname}_postroute.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
endif
- if ( -f antenna.out && ( -M antenna.out > -M ${rootname}_unroute.def )) then
- echo "Running annotate.tcl antenna.out ${synthdir}/${rootname}.rtlnopwr.v" \
+ echo "${bindir}/vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlbb.anno.v ${rootname}_postroute.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlbb.anno.v ${rootname}_postroute.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "Running vlog2Spice." |& tee -a ${synthlog}
+ echo "vlog2Spice -i -l ${spicepath} -o ${rootname}.anno.spc ${rootname}.rtl.anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Spice -i -l ${spicepath} -o ${rootname}.anno.spc \
+ ${rootname}.rtl.anno.v >>& ${synthlog}
+
+ set errcond = ${status}
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Spice failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ #------------------------------------------------------------------
+ # Spot check: Did vlog2Verilog or vlog2Spice exit with an error?
+ #------------------------------------------------------------------
+
+ if ( ! -f ${rootname}.rtl.anno.v || ( -M ${rootname}.rtl.anno.v \
+ < -M ${rootname}_postroute.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtl.anno.v created." \
|& tee -a ${synthlog}
- echo " ${synthdir}/${rootname}.spc ${synthdir}/${rootname}.rtlnopwr.anno.v" \
+ endif
+
+ if ( ! -f ${rootname}.rtlnopwr.anno.v || ( -M ${rootname}.rtlnopwr.anno.v \
+ < -M ${rootname}_postroute.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlnopwr.anno.v created." \
|& tee -a ${synthlog}
- echo " ${synthdir}/${rootname}.anno.spc ${spicepath} ${synthdir}/${rootname}_powerground" \
+ endif
+
+ if ( ! -f ${rootname}.rtlbb.anno.v || ( -M ${rootname}.rtlbb.anno.v \
+ < -M ${rootname}_postroute.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlbb.anno.v created." \
|& tee -a ${synthlog}
- ${scriptdir}/annotate.tcl antenna.out \
- ${synthdir}/${rootname}.rtlnopwr.v \
- ${synthdir}/${rootname}.spc \
- ${synthdir}/${rootname}.rtlnopwr.anno.v \
- ${synthdir}/${rootname}.anno.spc ${spicepath} \
- ${synthdir}/${rootname}_powerground |& tee -a ${synthlog}
-
- set errcond = $status
- if ( ${errcond} != 0 ) then
- echo "annotate.tcl failed with exit status ${errcond}" |& tee -a ${synthlog}
- echo "Premature exit." |& tee -a ${synthlog}
- echo "Synthesis flow stopped on error condition." >>& ${synthlog}
- exit 1
- endif
+ endif
- # If the antenna.out file contained only unfixed errors, then
- # the annotated output files may not exist, so check.
- if ( -f ${synthdir}/${rootname}.rtlnopwr.anno.v ) then
- mv ${synthdir}/${rootname}.rtlnopwr.anno.v ${synthdir}/${rootname}.rtlnopwr.v
- endif
- if ( -f ${synthdir}/${rootname}.rtl.anno.v ) then
- mv ${synthdir}/${rootname}.rtl.anno.v ${synthdir}/${rootname}.rtl.v
- endif
- if ( -f ${synthdir}/${rootname}.rtlbb.anno.v ) then
- mv ${synthdir}/${rootname}.rtlbb.anno.v ${synthdir}/${rootname}.rtlbb.v
- endif
- if ( -f ${synthdir}/${rootname}.anno.spc ) then
- mv ${synthdir}/${rootname}.anno.spc ${synthdir}/${rootname}.spc
- endif
- else
- echo "No antenna.out file generated, no need to annotate netlists." \
+ if ( ! -f ${rootname}.anno.spc || ( -M ${rootname}.anno.spc \
+ < -M ${rootname}_postroute.v )) then
+ echo "vlog2Spice failure: No file ${rootname}.anno.spc created." \
|& tee -a ${synthlog}
endif
+
+ # Back to the layout directory
+ cd ${layoutdir}
+
+ # If the antenna.out file contained only unfixed errors, then
+ # the annotated output files may not exist, so check.
+ if ( -f ${synthdir}/${rootname}.rtlnopwr.anno.v ) then
+ mv ${synthdir}/${rootname}.rtlnopwr.anno.v ${synthdir}/${rootname}.rtlnopwr.v
+ endif
+ if ( -f ${synthdir}/${rootname}.rtl.anno.v ) then
+ mv ${synthdir}/${rootname}.rtl.anno.v ${synthdir}/${rootname}.rtl.v
+ endif
+ if ( -f ${synthdir}/${rootname}.rtlbb.anno.v ) then
+ mv ${synthdir}/${rootname}.rtlbb.anno.v ${synthdir}/${rootname}.rtlbb.v
+ endif
+ if ( -f ${synthdir}/${rootname}.anno.spc ) then
+ mv ${synthdir}/${rootname}.anno.spc ${synthdir}/${rootname}.spc
+ endif
endif
#---------------------------------------------------------------------
diff --git a/scripts/replace.sh b/scripts/replace.sh
new file mode 100755
index 0000000..6e7c47e
--- /dev/null
+++ b/scripts/replace.sh
@@ -0,0 +1,747 @@
+#!/usr/bin/tcsh -f
+#----------------------------------------------------------
+# Placement script using abk-openroad RePlAce
+#
+#----------------------------------------------------------
+# Tim Edwards, 12/26/18, for Open Circuit Design
+#----------------------------------------------------------
+
+if ($#argv < 2) then
+ echo Usage: replace.sh <project_path> <source_name>
+ exit 1
+endif
+
+# Split out options from the main arguments
+set argline=(`getopt "kdf" $argv[1-]`)
+set cmdargs=`echo "$argline" | awk 'BEGIN {FS = "-- "} END {print $2}'`
+set argc=`echo $cmdargs | wc -w`
+
+if ($argc == 2) then
+ set argv1=`echo $cmdargs | cut -d' ' -f1`
+ set argv2=`echo $cmdargs | cut -d' ' -f2`
+else
+ echo Usage: replace.sh [options] <project_path> <source_name>
+ echo where
+ echo <project_path> is the name of the project directory containing
+ echo a file called qflow_vars.sh.
+ echo <source_name> is the root name of the verilog file, and
+ echo [options] are:
+ echo -k keep working files
+ echo -d generate DEF file for routing
+ echo -f final placement. Don't run if routing succeeded
+ echo
+ echo Options to specific tools can be specified with the following
+ echo variables in project_vars.sh:
+ echo
+ echo
+ exit 1
+endif
+
+set keep=0
+set makedef=0
+set final = 0
+
+foreach option (${argline})
+ switch (${option})
+ case -k:
+ set keep=1
+ breaksw
+ case -d:
+ set makedef=1
+ breaksw
+ case -f:
+ set final=1
+ breaksw
+ case --:
+ break
+ endsw
+end
+
+set projectpath=$argv1
+set sourcename=$argv2
+set rootname=${sourcename:h}
+
+# This script is called with the first argument <project_path>, which should
+# have file "qflow_vars.sh". Get all of our standard variable definitions
+# from the qflow_vars.sh file.
+
+if (! -f ${projectpath}/qflow_vars.sh ) then
+ echo "Error: Cannot find file qflow_vars.sh in path ${projectpath}"
+ exit 1
+endif
+
+source ${projectpath}/qflow_vars.sh
+source ${techdir}/${techname}.sh
+cd ${projectpath}
+if (-f project_vars.sh) then
+ source project_vars.sh
+endif
+
+# Get power and ground bus names
+if (-f ${synthdir}/${rootname}_powerground) then
+ source ${synthdir}/${rootname}_powerground
+endif
+
+# Prepend techdir to each leffile unless leffile begins with "/"
+set lefpath=""
+foreach f (${leffile})
+ set abspath=`echo ${f} | cut -c1`
+ if ( "${abspath}" == "/" ) then
+ set p=${leffile}
+ else
+ set p=${techdir}/${leffile}
+ endif
+ set lefpath="${lefpath} $p"
+end
+
+# Prepend techdir to techleffile unless techleffile begins with "/"
+set abspath=`echo ${techleffile} | cut -c1`
+if ( "${abspath}" == "/" ) then
+ set techlefpath=${techleffile}
+else
+ set techlefpath=${techdir}/${techleffile}
+endif
+
+# Prepend techdir to each spicefile unless spicefile begins with "/"
+set spicepath=""
+foreach f (${spicefile})
+ set abspath=`echo ${f} | cut -c1`
+ if ( "${abspath}" == "/" ) then
+ set p=${spicefile}
+ else
+ set p=${techdir}/${spicefile}
+ endif
+ set spicepath="${spicepath} $p"
+end
+
+# Add hard macros to spice path
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ # Too bad SPICE doesn't have an agreed-upon extension. Common ones are:
+ if ( ${file:e} == "sp" || ${file:e} == "spc" || \
+ ${file:e} == "spice" || ${file:e} == "cdl" || \
+ ${file:e} == "ckt" || ${file:e} == "net") then
+ set spicepath="${spicepath} -l ${sourcedir}/${macro_path}/${file}"
+ break
+ endif
+ end
+ end
+endif
+
+if (! ${?qrouter_nocleanup} ) then
+ set qrouter_nocleanup = ""
+else
+ if (${qrouter_nocleanup} == 0) then
+ set qrouter_nocleanup = ""
+ else
+ set qrouter_nocleanup = "true"
+ endif
+endif
+
+if (!($?logdir)) then
+ set logdir=${projectpath}/log
+endif
+mkdir -p ${logdir}
+set lastlog=${logdir}/synth.log
+set synthlog=${logdir}/place.log
+rm -f ${synthlog} >& /dev/null
+rm -f ${logdir}/sta.log >& /dev/null
+rm -f ${logdir}/route.log >& /dev/null
+rm -f ${logdir}/post_sta.log >& /dev/null
+rm -f ${logdir}/migrate.log >& /dev/null
+rm -f ${logdir}/drc.log >& /dev/null
+rm -f ${logdir}/lvs.log >& /dev/null
+rm -f ${logdir}/gdsii.log >& /dev/null
+touch ${synthlog}
+set date=`date`
+echo "Qflow placement logfile created on $date" > ${synthlog}
+
+#----------------------------------------------------------
+# Done with initialization
+#----------------------------------------------------------
+
+# Check if last line of log file says "error condition"
+set errcond = `tail -1 ${lastlog} | grep "error condition" | wc -l`
+if ( ${errcond} == 1 ) then
+ echo "Synthesis flow stopped on error condition. Placement will not proceed"
+ echo "until error condition is cleared."
+ exit 1
+endif
+
+#---------------------------------------------------
+# Create .info file from qrouter
+#---------------------------------------------------
+
+cd ${layoutdir}
+
+# First prepare a simple .cfg file that can be used to point qrouter
+# to the LEF files when generating layer information using the "-i" option.
+# This also contains the scaling units used in the .cel and .def files.
+
+#------------------------------------------------------------------
+# Determine the version number and availability of scripting
+# in qrouter.
+#------------------------------------------------------------------
+
+set version=`${bindir}/qrouter -v 0 -h | tail -1`
+set major=`echo $version | cut -d. -f1`
+set minor=`echo $version | cut -d. -f2`
+set subv=`echo $version | cut -d. -f3`
+set scripting=`echo $version | cut -d. -f4`
+
+# Create the initial (bootstrap) configuration file
+
+if ( $scripting == "T" ) then
+ if ( "$techleffile" == "" ) then
+ echo "read_lef ${lefpath}" > ${rootname}.cfg
+ else
+ echo "read_lef ${techlefpath}" > ${rootname}.cfg
+ endif
+else
+ if ( "$techleffile" == "" ) then
+ echo "lef ${lefpath}" > ${rootname}.cfg
+ else
+ echo "lef ${techlefpath}" > ${rootname}.cfg
+ endif
+endif
+
+${bindir}/qrouter -i ${rootname}.info -c ${rootname}.cfg
+
+#---------------------------------------------------------------------
+# Spot check: Did qrouter produce file ${rootname}.info?
+#---------------------------------------------------------------------
+
+if ( ! -f ${rootname}.info || \
+ ( -M ${rootname}.info < -M ${rootname}.pin )) then
+ echo "qrouter (-i) failure: No file ${rootname}.info." |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
+
+# Pull scale units from the .info file. Default units are centimicrons.
+set units=`cat ${rootname}.info | grep units | cut -d' ' -f3`
+if ( "${units}" == "" ) then
+ set units=100
+endif
+
+#-------------------------------------------------------------------------
+# Create the pre-placement .def file for RePlAce
+#-------------------------------------------------------------------------
+
+cd ${projectpath}
+
+if ( "$techleffile" == "" ) then
+ set lefoptions=""
+ set reploptions=""
+else
+ set lefoptions="-l ${techlefpath}"
+ set reploptions="-lef ${techlefpath}"
+endif
+set lefoptions="${lefoptions} -l ${lefpath}"
+set reploptions="${reploptions} -lef ${lefpath}"
+
+# Pass additional .lef files to vlog2Def and DEF2Verilog from the hard macros list
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ if ( ${file:e} == "lef" ) then
+ set lefoptions="${lefoptions} -l ${sourcedir}/${macro_path}/${file}"
+ endif
+ end
+ end
+endif
+
+if ( ${?initial_density} ) then
+ set vlog2defopts = "-d ${initial_density}"
+else
+ set vlog2defopts = ""
+endif
+
+echo "Running vlog2Def to generate input files for graywolf" |& tee -a ${synthlog}
+echo "vlog2Def ${lefoptions} -u $units ${vlog2defopts} -o ${layoutdir}/${rootname}_preplace.def ${synthdir}/${rootname}.rtlnopwr.v" |& tee -a ${synthlog}
+
+${bindir}/vlog2Def ${lefoptions} -u $units ${vlog2defopts} -o ${layoutdir}/${rootname}_preplace.def \
+ ${synthdir}/${rootname}.rtlnopwr.v >>& ${synthlog}
+
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "vlog2Def failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
+#---------------------------------------------------------------------
+# Spot check: Did vlog2Def produce file ${rootname}_preplace.def?
+#---------------------------------------------------------------------
+
+if ( ! -f ${layoutdir}/${rootname}_preplace.def || \
+ ( -M ${layoutdir}/${rootname}_preplace.def \
+ < -M ${rootname}.rtlnopwr.v )) then
+ echo "vlog2Def failure: No file ${rootname}_preplace.def." |& tee -a ${synthlog}
+ echo "vlog2Def was called with arguments: ${lefpath} "
+ echo " -u $units -o ${layoutdir}/${rootname}_preplace.def"
+ echo " ${synthdir}/${rootname}.rtlnopwr.v"
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
+
+#---------------------------------------------------------------------
+# Move to the layout directory
+#---------------------------------------------------------------------
+
+cd ${layoutdir}
+
+#---------------------------------------------------------------------
+# To do: Add behavior to manually define blockages.
+#---------------------------------------------------------------------
+
+# Set value ${fillers} to be equal either to a single cell name if
+# only one of (decapcell, antennacell, fillcell, bodytiecell) is
+# defined, or a comma-separated quadruplet. If only one is defined,
+# then "fillcell" is set to that name for those scripts that only
+# handle one kind of fill cell.
+
+# Make sure all cell types are defined, but an empty string if not used.
+
+if ( ! ${?fillcell} ) then
+ set fillcell = ""
+endif
+if ( ! ${?decapcell} ) then
+ set decapcell = ""
+endif
+if ( ! ${?antennacell} ) then
+ set antennacell = ""
+endif
+if ( ! ${?bodytiecell} ) then
+ set bodytiecell = ""
+endif
+
+set fillers = "${fillcell},${decapcell},${antennacell},${bodytiecell}"
+
+# For tools that only require one fill cell as option, make sure that
+# there is a valid fill cell type
+if ("x$fillcell" == "x") then
+ set fillcell = $decapcell
+endif
+if ("x$fillcell" == "x") then
+ set fillcell = $antennacell
+endif
+if ("x$fillcell" == "x") then
+ set fillcell = $bodytiecell
+endif
+if ("x$fillcell" == "x") then
+ # There is no fill cell, which is likely to produce poor results.
+ echo "Warning: No fill cell types are defined in the tech setup script."
+ echo "This is likely to produce poor layout and/or poor routing results."
+endif
+
+#---------------------------------------------------------------------
+# To do: Define a way to pass pin placement hints to RePlAce
+#---------------------------------------------------------------------
+
+#-----------------------------------------------
+# 1) Run RePlAce
+#-----------------------------------------------
+
+if ( !( ${?replace_options} )) then
+ # Some defaults (to be refined)
+ set replace_options = "-bmflag ispd"
+ set replace_options = "${replace_options} ${reploptions}"
+ set replace_options = "${replace_options} -def ${layoutdir}/${rootname}_preplace.def"
+ set replace_options = "${replace_options} -output outputs"
+ set replace_options = "${replace_options} -dpflag NTU3"
+ set replace_options = "${replace_options} -dploc ${bindir}/ntuplace3"
+endif
+
+#----------------------------------------------------------------------------
+# Set -den option to RePlAce if initial_density is defined in project_vars.sh
+
+if ( ${?initial_density} ) then
+ set replace_options = "${replace_options} -den ${initial_density}"
+endif
+
+echo "Running RePlAce placement" |& tee -a ${synthlog}
+echo "RePlAce ${replace_options}" |& tee -a ${synthlog}
+${bindir}/RePlAce ${replace_options} >>& ${synthlog}
+
+set errcond = $status
+if ( ${errcond} != 0 ) then
+ echo "RePlAce failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+endif
+
+#---------------------------------------------------------------------
+# Spot check: Did RePlAce produce file ${rootname}_preplace_final.def?
+#---------------------------------------------------------------------
+
+set outfile=`ls outputs/ispd/${rootname}_preplace/experiment*/${rootname}_preplace_final.def --sort=time | head -1`
+
+if ( ! -f ${outfile} || \
+ ( -M ${outfile} < -M ${rootname}_preplace.def )) then
+ echo "RePlAce failure: No file ${rootname}_preplace_final.def." |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
+
+echo "Copying RePlAce result up to layout directory:" |& tee -a ${synthlog}
+echo "cp ${outfile} ${rootname}.def" |& tee -a ${synthlog}
+cp ${outfile} ${rootname}.def
+
+#---------------------------------------------------
+# Remove RePlAce working files
+#---------------------------------------------------
+
+if ($keep == 0) then
+ rm -rf outputs
+endif
+
+#---------------------------------------------------------------
+# 2) Run clock tree synthesis and back-annotate netlists
+#---------------------------------------------------------------
+
+#---------------------------------------------------
+# NOTE: This should be in the qrouter script. . .
+# 3) Prepare .cfg file for qrouter
+#---------------------------------------------------
+
+if ($makedef == 1) then
+
+ echo "Running getantennacell to determine cell to use for antenna anchors." \
+ |& tee -a ${synthlog}
+ echo "getantennacell.tcl $rootname ${lefpath} $antennacell" |& tee -a ${synthlog}
+ set useantennacell=`${scriptdir}/getantennacell.tcl $rootname \
+ ${lefpath} $antennacell | grep antenna= | cut -d= -f2 | cut -d/ -f1`
+
+ if ( "${useantennacell}" != "" ) then
+ echo "Using cell ${useantennacell} for antenna anchors" |& tee -a ${synthlog}
+ endif
+
+ #---------------------------------------------------------------------
+ # Add spacer cells to create a straight border on the right side
+ # Add power stripes, stretching the cell if specified
+ #---------------------------------------------------------------------
+
+ if ( !(${?nospacers}) && (-f ${bindir}/addspacers) ) then
+
+ # Fill will use just the fillcell for padding under power buses
+ # and on the edges (to do: refine this to use other spacer types
+ # if the width options are more flexible).
+
+ if ( !( ${?addspacers_options} )) then
+ set addspacers_options = ""
+ endif
+ set addspacers_options = "${addspacers_options} -p ${vddnet} -g ${gndnet} -f ${fillcell} -O"
+
+ echo "Running addspacers to generate power stripes and align cell right edge" \
+ |& tee -a ${synthlog}
+ echo "addspacers ${addspacers_options} ${lefoptions} -o ${rootname}_filled.def ${rootname}" \
+ |& tee -a ${synthlog}
+
+ rm -f ${rootname}_filled.def
+ ${bindir}/addspacers ${addspacers_options} ${lefoptions} \
+ -o ${rootname}_filled.def ${rootname} >>& ${synthlog}
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "addspacers failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ if ( -f ${rootname}_filled.def ) then
+ mv ${rootname}_filled.def ${rootname}.def
+ endif
+
+ if ( -f ${rootname}.obsx ) then
+ # If addspacers annotated the .obs (obstruction) file, then
+ # overwrite the original.
+ mv ${rootname}.obsx ${rootname}.obs
+ endif
+ endif
+
+ # Copy the .def file to a backup called "unroute"
+ cp ${rootname}.def ${rootname}_unroute.def
+
+ # If the user didn't specify a number of layers for routing as part of
+ # the project variables, then the info file created by qrouter will have
+ # as many lines as there are route layers defined in the technology LEF
+ # file.
+
+ if ( !( ${?route_layers} )) then
+ set route_layers = `cat ${rootname}.info | grep -e horizontal -e vertical | wc -l`
+ endif
+
+ # Create the main configuration file
+
+ # Variables "via_pattern" (none, normal, invert), "via_stacks",
+ # and "via_use" can be specified in the tech script, and are
+ # appended to the qrouter configuration file. via_stacks defaults
+ # to 2 if not specified. It can be overridden from the user's .cfg2
+ # file.
+
+ if (${scripting} == "T") then
+ echo "# qrouter runtime script for project ${rootname}" > ${rootname}.cfg
+ echo "" >> ${rootname}.cfg
+ echo "verbose 1" >> ${rootname}.cfg
+ if ( "$techleffile" != "" ) then
+ echo "read_lef ${techlefpath}" >> ${rootname}.cfg
+ endif
+ echo "read_lef ${lefpath}" >> ${rootname}.cfg
+
+ if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ if ( ${file:e} == "lef" ) then
+ echo "read_lef ${sourcedir}/${macro_path}/${file}" >> ${rootname}.cfg
+ endif
+ end
+ end
+ endif
+
+ echo "catch {layers ${route_layers}}" >> ${rootname}.cfg
+ if ( ${?via_use} ) then
+ echo "" >> ${rootname}.cfg
+ echo "via use ${via_use}" >> ${rootname}.cfg
+ endif
+ if ( ${?via_pattern} ) then
+ echo "" >> ${rootname}.cfg
+ echo "via pattern ${via_pattern}" >> ${rootname}.cfg
+ endif
+ if (! ${?via_stacks} ) then
+ set via_stacks="all"
+ endif
+ echo "via stack ${via_stacks}" >> ${rootname}.cfg
+ echo "vdd $vddnet" >> ${rootname}.cfg
+ echo "gnd $gndnet" >> ${rootname}.cfg
+
+ else
+ echo "# qrouter configuration for project ${rootname}" > ${rootname}.cfg
+ echo "" >> ${rootname}.cfg
+ if ( "$techleffile" != "" ) then
+ echo "lef ${techlefpath}" >> ${rootname}.cfg
+ endif
+ echo "lef ${lefpath}" >> ${rootname}.cfg
+ if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ if ( ${file:e} == "lef" ) then
+ echo "lef ${sourcedir}/${macro_path}/${file}" >> ${rootname}.cfg
+ endif
+ end
+ end
+ endif
+ echo "num_layers ${route_layers}" >> ${rootname}.cfg
+ if ( ${?via_pattern} ) then
+ echo "" >> ${rootname}.cfg
+ echo "via pattern ${via_pattern}" >> ${rootname}.cfg
+ endif
+ if ( ${?via_stacks} ) then
+ if (${via_stacks} == "none") then
+ echo "no stack" >> ${rootname}.cfg
+ else
+ if (${via_stacks} == "all") then
+ echo "stack ${route_layers}" >> ${rootname}.cfg
+ else
+ echo "stack ${via_stacks}" >> ${rootname}.cfg
+ endif
+ endif
+ endif
+ endif
+
+ # Add obstruction fence around design, created by place2def.tcl
+ # and modified by addspacers
+
+ if ( -f ${rootname}.obs ) then
+ cat ${rootname}.obs >> ${rootname}.cfg
+ endif
+
+ # Scripted version continues with the read-in of the DEF file
+
+ if (${scripting} == "T") then
+ if ("x$useantennacell" != "x") then
+ echo "catch {qrouter::antenna init ${useantennacell}}" >> ${rootname}.cfg
+ endif
+ echo "read_def ${rootname}.def" >> ${rootname}.cfg
+ endif
+
+ # If there is a file called ${rootname}.cfg2, then append it to the
+ # ${rootname}.cfg file. It will be used to define all routing behavior.
+ # Otherwise, if using scripting, then append the appropriate routing
+ # command or procedure based on whether this is a pre-congestion
+ # estimate of routing or the final routing pass.
+
+ if ( -f ${rootname}.cfg2 ) then
+ cat ${rootname}.cfg2 >> ${rootname}.cfg
+ else
+ if (${scripting} == "T") then
+ echo "qrouter::standard_route ${rootname}_route.def false ${qrouter_nocleanup}" >> ${rootname}.cfg
+ # write_delays folded into standard_route in qrouter version 1.4.21.
+ if (${major} == 1 && ${minor} == 4 && ${subv} < 21) then
+ echo "qrouter::write_delays ${rootname}_route.rc" >> ${rootname}.cfg
+ endif
+ # Qrouter will drop into the interpreter on failure, so force a
+ # quit command to make sure that qrouter actually exits.
+ echo "quit" >> ${rootname}.cfg
+ endif
+ endif
+
+ #------------------------------------------------------------------
+ # Automatic optimization of buffer tree placement causes the
+ # original BLIF netlist, with tentative buffer assignments, to
+ # be invalid. Use the DEF2Verilog tool to back-annotate the
+ # correct assignments into the original BLIF netlist, then
+ # use that BLIF netlist to regenerate the SPICE and RTL verilog
+ # netlists.
+ #------------------------------------------------------------------
+
+ echo "DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v -o ${synthdir}/${rootname}_anno.v" \
+ |& tee -a ${synthlog}
+ echo "-p ${vddnet} -g ${gndnet} ${lefoptions} ${rootname}.def" |& tee -a ${synthlog}
+ ${bindir}/DEF2Verilog -v ${synthdir}/${rootname}.rtlnopwr.v \
+ -o ${synthdir}/${rootname}_anno.v \
+ -p ${vddnet} -g ${gndnet} \
+ ${lefoptions} ${rootname}.def >>& ${synthlog}
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "DEF2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ #------------------------------------------------------------------
+ # Spot check: Did DEF2Verilog produce an output file?
+ #------------------------------------------------------------------
+
+ if ( !( -f ${synthdir}/${rootname}_anno.v )) then
+ echo "DEF2Verilog failure: No file ${rootname}_anno.v." \
+ |& tee -a ${synthlog}
+ echo "RTL verilog and SPICE netlists may be invalid if there" \
+ |& tee -a ${synthlog}
+ echo "were buffer trees optimized by placement." |& tee -a ${synthlog}
+ echo "Synthesis flow continuing, condition not fatal." >> ${synthlog}
+ else
+ echo "" >> ${synthlog}
+ echo "Generating RTL verilog and SPICE netlist file in directory" \
+ |& tee -a ${synthlog}
+ echo " ${synthdir}" |& tee -a ${synthlog}
+ echo "Files:" |& tee -a ${synthlog}
+ echo " Verilog: ${synthdir}/${rootname}.rtl.v" |& tee -a ${synthlog}
+ echo " Verilog: ${synthdir}/${rootname}.rtlnopwr.v" |& tee -a ${synthlog}
+ echo " Verilog: ${synthdir}/${rootname}.rtlbb.v" |& tee -a ${synthlog}
+ echo " Spice: ${synthdir}/${rootname}.spc" |& tee -a ${synthlog}
+ echo "" >> ${synthlog}
+
+ cd ${synthdir}
+
+ #------------------------------------------------------------------
+ # Copy the original rtl.v and rtlnopwr.v for use in comparison of
+ # pre- and post-placement netlists.
+ #------------------------------------------------------------------
+
+ echo "Copying ${rootname}.rtl.v, ${rootname}.rtlnopwr.v, and ${rootname}.rtlbb.v to backups"
+ cp ${rootname}.rtl.v ${rootname}_synth.rtl.v
+ cp ${rootname}.rtlnopwr.v ${rootname}_synth.rtlnopwr.v
+ cp ${rootname}.rtlbb.v ${rootname}_synth.rtlbb.v
+
+ echo "Running vlog2Verilog." |& tee -a ${synthlog}
+ echo "vlog2Verilog -c -v ${vddnet} -g ${gndnet} -o ${rootname}.rtl.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtl.v ${rootname}_anno.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlnopwr.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlnopwr.v ${rootname}_anno.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} -o ${rootname}.rtlbb.v ${rootname}_anno.v" |& tee -a ${synthlog}
+ ${bindir}/vlog2Verilog -c -b -p -n -v ${vddnet} -g ${gndnet} \
+ -o ${rootname}.rtlbb.v ${rootname}_anno.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ echo "Running vlog2Spice." |& tee -a ${synthlog}
+ echo "vlog2Spice -i -l ${spicepath} -o ${rootname}.spc ${rootname}.rtl.v" \
+ |& tee -a ${synthlog}
+ ${bindir}/vlog2Spice -i -l ${spicepath} -o ${rootname}.spc ${rootname}.rtl.v >>& ${synthlog}
+
+ set errcond = $status
+ if ( ${errcond} != 0 ) then
+ echo "vlog2Spice failed with exit status ${errcond}" |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped on error condition." >>& ${synthlog}
+ exit 1
+ endif
+
+ #------------------------------------------------------------------
+ # Spot check: Did vlog2Verilog or vlog2Spice exit with an error?
+ #------------------------------------------------------------------
+
+ if ( ! -f ${rootname}.rtl.v || ( -M ${rootname}.rtl.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtl.v created." \
+ |& tee -a ${synthlog}
+ endif
+
+ if ( ! -f ${rootname}.rtlnopwr.v || ( -M ${rootname}.rtlnopwr.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlnopwr.v created." \
+ |& tee -a ${synthlog}
+ endif
+
+ if ( ! -f ${rootname}.rtlbb.v || ( -M ${rootname}.rtlbb.v \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Verilog failure: No file ${rootname}.rtlbb.v created." \
+ |& tee -a ${synthlog}
+ endif
+
+ if ( ! -f ${rootname}.spc || ( -M ${rootname}.spc \
+ < -M ${rootname}_anno.v )) then
+ echo "vlog2Spice failure: No file ${rootname}.spc created." \
+ |& tee -a ${synthlog}
+ endif
+
+ # Return to the layout directory
+ cd ${layoutdir}
+
+ endif
+endif
+
+#------------------------------------------------------------
+# Done!
+#------------------------------------------------------------
+
+set endtime = `date`
+echo "Placement script ended on $endtime" >> $synthlog
+
+exit 0
diff --git a/scripts/spi2xspice.py.in b/scripts/spi2xspice.py.in
index ac46f37..4b8105f 100755
--- a/scripts/spi2xspice.py.in
+++ b/scripts/spi2xspice.py.in
@@ -140,13 +140,21 @@ def write_models(cellsused, celldefs, ofile, timing):
psubs = psubs.replace(bchar, bval)
# Handle the awkward syntax where, e.g., (A B) means (A & B)
- psubs = imprex.sub('\g<1>&\g<2>', psubs)
+ # Must be done in a loop because re.sub does not work on
+ # overlapping matches.
+
+ while True:
+ psubbed = imprex.sub('\g<1>&\g<2>', psubs)
+ if psubbed == psubs:
+ break
+ psubs = psubbed
try:
tval = eval('(' + psubs + ')&1')
except (SyntaxError, NameError):
tabstr = ''
print("Could not evaluate function " + cellrec['function'][k])
+ print("(Evaluated as " + psubs + ")")
break
if tval:
@@ -272,7 +280,9 @@ def read_spice(filein, fileout, celldefs, debug, modelfile, timing):
if xmatch:
# Replace subcircuit call with xspice call
instname = xmatch.group(1)
- conns = xmatch.group(2).split()
+ # NOTE: Parsing for common CDLisms like '/' before cellname and
+ # parameter passing to the instance.
+ conns = list(item for item in xmatch.group(2).split() if '=' not in item and item != '/')
pins = conns[0:-1]
cellname = conns[-1]
@@ -485,9 +495,12 @@ def read_spice(filein, fileout, celldefs, debug, modelfile, timing):
i = cellrec['spicepins'].index(subpin)
# "pins[i]" is the name of the connecting net
# on the top level
- inlist.append(pins[i])
- if pins[i] in pindefs:
- pindefs[pins[i]] = 'input'
+ if len(pins) > i:
+ inlist.append(pins[i])
+ if pins[i] in pindefs:
+ pindefs[pins[i]] = 'input'
+ else:
+ print('Pin ' + subpin + ' of subckt ' + cellname + ' does not have a connecting net', file=sys.stderr)
if 'outputs' in cellrec:
for subpin in cellrec['outputs']:
@@ -495,9 +508,12 @@ def read_spice(filein, fileout, celldefs, debug, modelfile, timing):
i = cellrec['spicepins'].index(subpin)
# "pins[i]" is the name of the connecting net
# on the top level
- outlist.append(pins[i])
- if pins[i] in pindefs:
- pindefs[pins[i]] = 'output'
+ if len(pins) > i:
+ outlist.append(pins[i])
+ if pins[i] in pindefs:
+ pindefs[pins[i]] = 'output'
+ else:
+ print('Pin ' + subpin + ' of subckt ' + cellname + ' does not have a connecting net', file=sys.stderr)
intext = ' '.join(inlist)
outtext = ' '.join(outlist)
diff --git a/scripts/textreport.py.in b/scripts/textreport.py.in
index 6d91ba7..73d3eb1 100755
--- a/scripts/textreport.py.in
+++ b/scripts/textreport.py.in
@@ -89,7 +89,7 @@ class TextReport(tkinter.Toplevel):
def add_text_from_file(self, filename):
print('Loading text text from file ' + filename)
- with open(filename, 'r') as f:
+ with open(filename, 'r', encoding='utf-8') as f:
self.text = f.read()
self.title = filename
self.display()
diff --git a/scripts/vesta.sh b/scripts/vesta.sh
index 5d3c467..15ebc89 100755
--- a/scripts/vesta.sh
+++ b/scripts/vesta.sh
@@ -158,16 +158,20 @@ if ($dodelays == 1) then
# Run rc2dly
echo "Converting qrouter output to vesta delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -d ${rootname}.dly" \
+ echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.dly" |& tee -a ${synthlog}
${bindir}/rc2dly -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.dly
# Run rc2dly again to get SPEF format file
echo "Converting qrouter output to SPEF delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -D : -r ${rootname}.rc -l ${libertypath} -d ${rootname}.spef" \
+ echo "Running rc2dly -D : -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.spef" |& tee -a ${synthlog}
${bindir}/rc2dly -D : -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.spef
# Translate <, >, and $ in file to _ to match the verilog. Make translations
@@ -184,9 +188,11 @@ if ($dodelays == 1) then
# Run rc2dly again to get SDF format file
echo "Converting qrouter output to SDF delay format" |& tee -a ${synthlog}
- echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -d ${rootname}.sdf" \
+ echo "Running rc2dly -r ${rootname}.rc -l ${libertypath} -V ${synthdir}/${rootname}.rtl.v" \
|& tee -a ${synthlog}
+ echo "-d ${rootname}.sdf" |& tee -a ${synthlog}
${bindir}/rc2dly -r ${rootname}.rc -l ${libertypath} \
+ -V ${synthdir}/${rootname}.rtl.v \
-d ${synthdir}/${rootname}.sdf
# Translate <, >, in file to [, ] to match the verilog (rtl.nopwr.v version).
@@ -202,8 +208,8 @@ if ($dodelays == 1) then
# Spot check for output file (NOTE: Currently not checking if SPEF
# or SDF format files were created)
- if ( !( -f ${rootname}.dly || \
- ( -M ${rootname}.dly < -M ${layoutdir}/${rootname}.rc ))) then
+ if ( ! -f ${rootname}.dly || \
+ ( -M ${rootname}.dly < -M ${layoutdir}/${rootname}.rc )) then
echo "rc2dly failure: No file ${rootname}.dly created." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
diff --git a/scripts/synthesize.sh b/scripts/yosys.sh
index 75031e9..3f9bae5 100755
--- a/scripts/synthesize.sh
+++ b/scripts/yosys.sh
@@ -1,9 +1,10 @@
#!/usr/bin/tcsh -f
#
-# synthesize.sh:
+# yosys.sh:
#-------------------------------------------------------------------------
#
# This script synthesizes verilog files for qflow using yosys
+# Prerequisites: Synthesizable verilog source file.
#
#-------------------------------------------------------------------------
# November 2006
@@ -23,7 +24,7 @@ if ($#argv == 2 || $#argv == 3) then
set sourcename=""
endif
else
- echo Usage: synthesize.sh <project_path> <module_name> [<source_file>]
+ echo Usage: yosys.sh <project_path> <module_name> [<source_file>]
echo
echo where
echo
@@ -42,7 +43,7 @@ else
echo $yosys_script for yosys
echo $nobuffers to ignore output buffering
echo $inbuffers to force input buffering
- echo $fanout_options for blifFanout
+ echo $fanout_options for vlogFanout
exit 1
endif
@@ -104,8 +105,25 @@ foreach f (${spicefile})
set spicepath="${spicepath} $p"
end
+# Add hard macros to spice path
+
+if ( ${?hard_macros} ) then
+ foreach macro_path ( $hard_macros )
+ foreach file ( `ls ${sourcedir}/${macro_path}` )
+ # Too bad SPICE doesn't have an agreed-upon extension. Common ones are:
+ if ( ${file:e} == "sp" || ${file:e} == "spc" || \
+ ${file:e} == "spice" || ${file:e} == "cdl" || \
+ ${file:e} == "ckt" || ${file:e} == "net") then
+ set spicepath="${spicepath} -l ${sourcedir}/${macro_path}/${file}"
+ break
+ endif
+ end
+ end
+endif
+
# Prepend techdir to leffile unless leffile begins with "/"
set lefpath=""
+set postproclefpath=""
foreach f (${leffile})
set abspath=`echo ${f} | cut -c1`
if ( "${abspath}" == "/" ) then
@@ -114,6 +132,7 @@ foreach f (${leffile})
set p=${techdir}/${leffile}
endif
set lefpath="${lefpath} $p"
+ set postproclefpath="${postproclefpath} -l $p"
end
# Determine version of yosys
@@ -140,17 +159,28 @@ else
endif
set minor = `echo ${minor} | sed 's/+//'`
+
# Check if "yosys_options" specifies a script to use for yosys.
if ( ! ${?yosys_options} ) then
set yosys_options = ""
endif
set usescript = `echo ${yosys_options} | grep -- -s | wc -l`
+cd ${sourcedir}
# Only generate yosys script if none is specified in the yosys options
if ( ${usescript} == 0 ) then
-cd ${sourcedir}
+# If there is a file ${modulename}_mapped.v, move it to a temporary
+# place so we can see if yosys generates a new one or not. Make
+# sure it does *not* have a .v suffix, or else the scripts that
+# search for a file containing the module may pick it up in favor
+# of the actual source file.
+
+rm -f ${modulename}_mapped.v.orig
+if ( -f ${modulename}_mapped.v ) then
+ mv ${modulename}_mapped.v ${modulename}_mapped.v.orig
+endif
# Check for filelists variable overriding the default. If it is set,
# use the value as the filename to get all the verilog source files
@@ -378,13 +408,10 @@ endif
# Generate the main yosys script
#---------------------------------------------------------------------
-set blif_opts = ""
-
-# Set option for generating buffers
-set blif_opts = "${blif_opts} -buf ${bufcell} ${bufpin_in} ${bufpin_out}"
+set vlog_opts = ""
# Set option for generating only the flattened top-level cell
-# set blif_opts = "${blif_opts} ${modulename}"
+# set vlog_opts = "${vlog_opts} ${modulename}"
cat > ${modulename}.ys << EOF
# Synthesis script for yosys created by qflow
@@ -393,7 +420,8 @@ EOF
# Support for structural verilog---any cell can be called as long as
# it has a liberty file entry to go along with it. Standard cells
# are supported automatically. All other cells should be put in the
-# "hard_macro" variable.
+# "hard_macro" variable. Also use the "setundef" command to ensure
+# that unconnected inputs are grounded.
cat >> ${modulename}.ys << EOF
read_liberty -lib -ignore_miss_dir -setattr blackbox ${libertypath}
@@ -494,6 +522,7 @@ if ( ${?abc_script} ) then
cat >> ${modulename}.ys << EOF
abc -exe ${bindir}/yosys-abc -liberty ${libertypath} -script ${abc_script}
flatten
+setundef -zero
EOF
else
@@ -502,6 +531,7 @@ EOF
cat >> ${modulename}.ys << EOF
abc -exe ${bindir}/yosys-abc -liberty ${libertypath}
flatten
+setundef -zero
EOF
endif
@@ -510,6 +540,7 @@ else
# Map combinatorial cells, standard script
abc -exe ${bindir}/yosys-abc -liberty ${libertypath} -script +strash;scorr;ifraig;retime,{D};strash;dch,-f;map,-M,1,{D}
flatten
+setundef -zero
EOF
endif
@@ -561,24 +592,16 @@ cat >> ${modulename}.ys << EOF
opt
clean
rename -enumerate
-write_blif ${blif_opts} ${modulename}_mapped.blif
+write_verilog ${vlog_opts} ${modulename}_mapped.v
stat
EOF
endif # Generation of yosys script if ${usescript} == 0
-
#---------------------------------------------------------------------
# Yosys synthesis
#---------------------------------------------------------------------
-# If there is a file ${modulename}_mapped.blif, move it to a temporary
-# place so we can see if yosys generates a new one or not.
-
-if ( -f ${modulename}_mapped.blif ) then
- mv ${modulename}_mapped.blif ${modulename}_mapped_orig.blif
-endif
-
echo "Running yosys for verilog parsing and synthesis" |& tee -a ${synthlog}
# If provided own script yosys call that, otherwise call yosys with generated script.
if ( ${usescript} == 1 ) then
@@ -590,23 +613,23 @@ else
endif
#---------------------------------------------------------------------
-# Spot check: Did yosys produce file ${modulename}_mapped.blif?
+# Spot check: Did yosys produce file ${modulename}_mapped.v?
#---------------------------------------------------------------------
-if ( !( -f ${modulename}_mapped.blif )) then
- echo "outputprep failure: No file ${modulename}_mapped.blif." \
+if ( !( -f ${modulename}_mapped.v )) then
+ echo "outputprep failure: No file ${modulename}_mapped.v." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
- # Replace the old blif file, if we had moved it
- if ( -f ${modulename}_mapped_orig.blif ) then
- mv ${modulename}_mapped_orig.blif ${modulename}_mapped.blif
+ # Replace the old verilog file, if we had moved it
+ if ( -f ${modulename}_mapped.v.orig ) then
+ mv ${modulename}_mapped.v.orig ${modulename}_mapped.v
endif
exit 1
else
- # Remove the old blif file, if we had moved it
- if ( -f ${modulename}_mapped_orig.blif ) then
- rm ${modulename}_mapped_orig.blif
+ # Remove the old verilog file, if we had moved it
+ if ( -f ${modulename}_mapped.v.orig ) then
+ rm ${modulename}_mapped.v.orig
endif
endif
@@ -643,7 +666,7 @@ echo set vddnet=\"${vddnet}\" > ${synthdir}/${modulename}_powerground
echo set gndnet=\"${gndnet}\" >> ${synthdir}/${modulename}_powerground
#----------------------------------------------------------------------
-# Run ypostproc syntax post-processor
+# Run post-processor
#----------------------------------------------------------------------
if (! $?postproc_options) then
@@ -651,92 +674,21 @@ if (! $?postproc_options) then
else
if ( $postproc_options == "-anchors" ) then
if ( $?antennacell ) then
- echo "Running getantennacell to determine cell to use for anchoring." |& tee -a ${synthlog}
- echo "getantennacell.tcl $modulename ${lefpath} $antennacell" |& tee -a ${synthlog}
- set anchorinfo=`${scriptdir}/getantennacell.tcl $modulename ${lefpath} $antennacell | grep antenna= | cut -d= -f2`
- set postproc_options = "-anchor=$anchorinfo"
+ set postproc_options = "-a $antennacell"
else
echo "Antenna anchoring requested but no antenna cell defined in tech."
set postproc_options = ""
endif
endif
endif
+set postproc_options="${postproclefpath} ${postproc_options}"
-echo "Cleaning up output syntax" |& tee -a ${synthlog}
-echo "ypostproc.tcl ${modulename}_mapped.blif ${modulename} ${techdir}/${techname}.sh ${vddnet} ${gndnet} ${postproc_options}" \
- |& tee -a ${synthlog}
-${scriptdir}/ypostproc.tcl ${modulename}_mapped.blif ${modulename} \
- ${techdir}/${techname}.sh ${vddnet} ${gndnet} ${postproc_options}
-set errcond = ${status}
-if ( $errcond != 0 ) then
- echo "ypostproc failure. See file ${synthlog} for error messages." \
- |& tee -a ${synthlog}
- echo "Premature exit." |& tee -a ${synthlog}
- echo "Synthesis flow stopped due to error condition." >> ${synthlog}
- exit 1
-endif
-
-#----------------------------------------------------------------------
-# Add buffers in front of all outputs (for yosys versions before 0.2.0)
-#----------------------------------------------------------------------
-
-if ( ${major} == 0 && ${minor} < 2 ) then
- if ($?nobuffers) then
- set final_blif = "${modulename}_mapped_tmp.blif"
- else
- echo "Adding output buffers"
- ${scriptdir}/ybuffer.tcl ${modulename}_mapped_tmp.blif \
- ${modulename}_mapped_buf.blif ${techdir}/${techname}.sh
- set final_blif = "${modulename}_mapped_buf.blif"
- endif
-else
- # Buffers already handled within yosys
- set final_blif = "${modulename}_mapped_tmp.blif"
-endif
-
-#---------------------------------------------------------------------
-# The following definitions will replace "LOGIC0" and "LOGIC1"
-# with buffers from gnd and vdd, respectively. This takes care
-# of technologies where tie-low and tie-high cells are not
-# defined.
-#---------------------------------------------------------------------
-
-echo "Cleaning up blif file syntax" |& tee -a ${synthlog}
-
-if ( "$tielo" == "") then
- set subs0a="/LOGIC0/s/O=/${bufpin_in}=gnd ${bufpin_out}=/"
- set subs0b="/LOGIC0/s/LOGIC0/${bufcell}/"
-else
- set subs0a=""
- set subs0b=""
-endif
-
-if ( "$tiehi" == "") then
- set subs1a="/LOGIC1/s/O=/${bufpin_in}=vdd ${bufpin_out}=/"
- set subs1b="/LOGIC1/s/LOGIC1/${bufcell}/"
-else
- set subs1a=""
- set subs1b=""
-endif
-
-#---------------------------------------------------------------------
-# Remove backslashes, references to "$techmap", and
-# make local input nodes of the form $0node<a:b><c> into the
-# form node<c>_FF_INPUT
-#---------------------------------------------------------------------
-
-cat ${final_blif} | sed \
- -e "$subs0a" -e "$subs0b" -e "$subs1a" -e "$subs1b" \
- -e 's/\\\([^$]\)/\1/g' \
- -e 's/$techmap//g' \
- -e 's/$0\([^ \t<]*\)<[0-9]*:[0-9]*>\([^ \t]*\)/\1\2_FF_INPUT/g' \
- > ${synthdir}/${modulename}.blif
-
-# Switch to synthdir for processing of the BDNET netlist
+# Switch to synthdir for processing of the RTL verilog netlist
+mv ${modulename}_mapped.v ${synthdir}
cd ${synthdir}
#---------------------------------------------------------------------
-# If "nofanout" is set, then don't run blifFanout.
+# If "nofanout" is set, then don't run vlogFanout.
#---------------------------------------------------------------------
if ($?nofanout) then
@@ -744,13 +696,6 @@ if ($?nofanout) then
else
#---------------------------------------------------------------------
-# Make a copy of the original blif file, as this will be overwritten
-# by the fanout handling process
-#---------------------------------------------------------------------
-
- cp ${modulename}.blif ${modulename}_bak.blif
-
-#---------------------------------------------------------------------
# Check all gates for fanout load, and adjust gate strengths as
# necessary. Iterate this step until all gates satisfy drive
# requirements.
@@ -769,10 +714,12 @@ else
set fanout_options=""
endif
- if (-f ${libertypath} && -f ${bindir}/blifFanout ) then
+ if (-f ${libertypath} && -f ${bindir}/vlogFanout ) then
- if ("x${separator}" == "x") then
+ if (! $?separator) then
set sepoption=""
+ else if ("x$separator" == "x") then
+ set sepoption='-s nullstring'
else
set sepoption="-s ${separator}"
endif
@@ -813,42 +760,56 @@ else
end
endif
- echo "Running blifFanout (iterative)" |& tee -a ${synthlog}
- echo "Each iteration calls:" |& tee -a ${synthlog}
- echo "blifFanout ${fanout_options} -I ${modulename}_nofanout ${sepoption} ${libertyoption} ${bufoption} tmp.blif ${modulename}.blif" |& tee -a ${synthlog}
+ echo "Running vlogFanout" |& tee -a ${synthlog}
+ echo "vlogFanout ${fanout_options} -I ${modulename}_nofanout ${sepoption} ${libertyoption} ${bufoption} ${modulename}_mapped.v ${modulename}_sized.v" |& tee -a ${synthlog}
echo "" >> ${synthlog}
- set nchanged=1000
- while ($nchanged > 0)
- mv ${modulename}.blif tmp.blif
- set nchanged = `${bindir}/blifFanout ${fanout_options} \
- -I ${modulename}_nofanout ${libertyoption} ${sepoption} \
- ${bufoption} tmp.blif ${modulename}.blif |& tee -a ${synthlog} | \
- grep "changed:" | cut -f5 -d' '`
-
- set errcond=$status
- if ( $errcond > 0 ) then
- echo "blifFanout failed with error condition ${errcond}." \
+ ${bindir}/vlogFanout ${fanout_options} \
+ -I ${modulename}_nofanout ${sepoption} ${libertyoption} \
+ ${bufoption} ${modulename}_mapped.v ${modulename}_sized.v |& tee -a ${synthlog}
+
+ set errcond = ${status}
+ if ( ${errcond} != 0 ) then
+ echo "vlogFanout failed with error condition ${errcond}." \
|& tee -a ${synthlog}
- echo "Premature exit." |& tee -a ${synthlog}
- echo "Synthesis flow stopped due to error condition." >> ${synthlog}
- exit 1
- else
- echo "gates resized: $nchanged" |& tee -a ${synthlog}
- endif
- end
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+ endif
else
- echo "blifFanout not run: No cell size optimization." |& tee -a ${synthlog}
- set nchanged=0
+ echo "vlogFanout not run: No cell size optimization." |& tee -a ${synthlog}
endif
endif
#---------------------------------------------------------------------
-# Spot check: Did blifFanout produce an error?
+# Spot check: Did vlogFanout produce an error?
#---------------------------------------------------------------------
-if ( $nchanged < 0 ) then
- echo "blifFanout failure. See file ${synthlog} for error messages." \
+if ( ! -f ${modulename}_sized.v || \
+ ( -M ${modulename}_sized.v < -M ${modulename}_mapped.v )) then
+ echo "vlogFanout failure. See file ${synthlog} for error messages." \
+ |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
+
+echo "Running vlog2Verilog for antenna cell mapping." |& tee -a ${synthlog}
+echo "vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} ${postproc_options}" \
+ |& tee -a ${synthlog}
+echo " -o ${modulename}.v ${modulename}_sized.v" |& tee -a ${synthlog}
+
+rm -f ${modulename}.v
+${bindir}/vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} ${postproc_options} \
+ -o ${modulename}.v ${modulename}_sized.v >>& ${synthlog}
+
+#---------------------------------------------------------------------
+# Spot check: Did vlog2Verilog produce an error?
+#---------------------------------------------------------------------
+
+if ( ! -f ${modulename}.v || \
+ ( -M ${modulename}.v < -M ${modulename}_sized.v )) then
+ echo "vlog2Verilog failure. See file ${synthlog} for error messages." \
|& tee -a ${synthlog}
echo "Premature exit." |& tee -a ${synthlog}
echo "Synthesis flow stopped due to error condition." >> ${synthlog}
@@ -866,70 +827,116 @@ echo " Verilog: ${synthdir}/${modulename}.rtlbb.v" |& tee -a ${synthlog}
echo " Spice: ${synthdir}/${modulename}.spc" |& tee -a ${synthlog}
echo "" >> ${synthlog}
-echo "Running blif2Verilog." |& tee -a ${synthlog}
-${bindir}/blif2Verilog -c -v ${vddnet} -g ${gndnet} ${modulename}.blif \
- > ${modulename}.rtl.v
+echo "Running vlog2Verilog." |& tee -a ${synthlog}
+echo "vlog2Verilog -c -v ${vddnet} -g ${gndnet} ${postproclefpath} \
+ -o ${modulename}.rtl.v" |& tee -a ${synthlog}
+echo " ${modulename}.v" |& tee -a ${synthlog}
+${bindir}/vlog2Verilog -c -v ${vddnet} -g ${gndnet} ${postproclefpath} \
+ -o ${modulename}.rtl.v ${modulename}.v >>& ${synthlog}
+
+set errcond = ${status}
+if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with error condition ${errcond}." \
+ |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
# Version without explicit power and ground
-${bindir}/blif2Verilog -c -p -v ${vddnet} -g ${gndnet} ${modulename}.blif \
- > ${modulename}.rtlnopwr.v
+echo "vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} ${postproclefpath} \
+ -o ${modulename}.rtlnopwr.v" |& tee -a ${synthlog}
+echo " ${modulename}.v" |& tee -a ${synthlog}
+${bindir}/vlog2Verilog -c -p -v ${vddnet} -g ${gndnet} ${postproclefpath} \
+ -o ${modulename}.rtlnopwr.v ${modulename}.v >>& ${synthlog}
+
+set errcond = ${status}
+if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with error condition ${errcond}." \
+ |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
# Version without vectors
-${bindir}/blif2Verilog -c -p -b -n -v ${vddnet} -g ${gndnet} ${modulename}.blif \
- > ${modulename}.rtlbb.v
+echo "${bindir}/vlog2Verilog -c -p -b -n -v ${vddnet} -g ${gndnet} ${postproclefpath}" \
+ |& tee -a ${synthlog}
+echo " -o ${modulename}.rtlbb.v" |& tee -a ${synthlog}
+${bindir}/vlog2Verilog -c -p -b -n -v ${vddnet} -g ${gndnet} ${postproclefpath} \
+ -o ${modulename}.rtlbb.v ${modulename}.v >>& ${synthlog}
+
+set errcond = ${status}
+if ( ${errcond} != 0 ) then
+ echo "vlog2Verilog failed with error condition ${errcond}." \
+ |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
#---------------------------------------------------------------------
-# Spot check: Did blif2Verilog exit with an error?
+# Spot check: Did vlog2Verilog exit with an error?
# Note that these files are not critical to the main synthesis flow,
# so if they are missing, we flag a warning but do not exit.
#---------------------------------------------------------------------
-if ( !( -f ${modulename}.rtl.v || \
- ( -M ${modulename}.rtl.v < -M ${modulename}.blif ))) then
- echo "blif2Verilog failure: No file ${modulename}.rtl.v created." \
+if ( ! -f ${modulename}.rtl.v || \
+ ( -M ${modulename}.rtl.v < -M ${modulename}.v )) then
+ echo "vlog2Verilog failure: No file ${modulename}.rtl.v created." \
|& tee -a ${synthlog}
endif
-if ( !( -f ${modulename}.rtlnopwr.v || \
- ( -M ${modulename}.rtlnopwr.v < -M ${modulename}.blif ))) then
- echo "blif2Verilog failure: No file ${modulename}.rtlnopwr.v created." \
+if ( ! -f ${modulename}.rtlnopwr.v || \
+ ( -M ${modulename}.rtlnopwr.v < -M ${modulename}.v )) then
+ echo "vlog2Verilog failure: No file ${modulename}.rtlnopwr.v created." \
|& tee -a ${synthlog}
endif
-if ( !( -f ${modulename}.rtlbb.v || \
- ( -M ${modulename}.rtlbb.v < -M ${modulename}.blif ))) then
- echo "blif2Verilog failure: No file ${modulename}.rtlbb.v created." \
+if ( ! -f ${modulename}.rtlbb.v || \
+ ( -M ${modulename}.rtlbb.v < -M ${modulename}.v )) then
+ echo "vlog2Verilog failure: No file ${modulename}.rtlbb.v created." \
|& tee -a ${synthlog}
endif
#---------------------------------------------------------------------
-echo "Running blif2BSpice." |& tee -a ${synthlog}
+echo "Running vlog2Spice." |& tee -a ${synthlog}
if ("x${spicefile}" == "x") then
set spiceopt=""
else
set spiceopt="-l ${spicepath}"
endif
-${bindir}/blif2BSpice -i -p ${vddnet} -g ${gndnet} ${spiceopt} \
- ${modulename}.blif > ${modulename}.spc
+echo "vlog2Spice -i ${spiceopt} -o ${modulename}.spc ${modulename}.rtl.v" |& tee -a ${synthlog}
+${bindir}/vlog2Spice -i ${spiceopt} -o ${modulename}.spc ${modulename}.rtl.v >>& ${synthlog}
+
+set errcond = ${status}
+if ( ${errcond} != 0 ) then
+ echo "vlog2Spice failed with error condition ${errcond}." \
+ |& tee -a ${synthlog}
+ echo "Premature exit." |& tee -a ${synthlog}
+ echo "Synthesis flow stopped due to error condition." >> ${synthlog}
+ exit 1
+endif
#---------------------------------------------------------------------
-# Spot check: Did blif2BSpice exit with an error?
+# Spot check: Did vlog2Spice exit with an error?
# Note that these files are not critical to the main synthesis flow,
# so if they are missing, we flag a warning but do not exit.
#---------------------------------------------------------------------
-if ( !( -f ${modulename}.spc || \
- ( -M ${modulename}.spc < -M ${modulename}.blif ))) then
- echo "blif2BSpice failure: No file ${modulename}.spc created." \
+if ( ! -f ${modulename}.spc || \
+ ( -M ${modulename}.spc < -M ${modulename}.v )) then
+ echo "vlog2Spice failure: No file ${modulename}.spc created." \
|& tee -a ${synthlog}
else
set test=`which python3`
if ( $status == 0 ) then
echo "Running spi2xspice.py" |& tee -a ${synthlog}
+
if (!( ${?xspice_options} )) then
- set xspice_options=""
+ set xspice_options=""
endif
# Add paths to liberty files for any hard macros
@@ -950,8 +957,8 @@ else
${scriptdir}/spi2xspice.py "${libertyoption}" ${xspice_options} \
${modulename}.spc ${modulename}.xspice
- if ( !( -f ${modulename}.xspice || \
- ( -M ${modulename}.xspice < -M ${modulename}.spc ))) then
+ if ( ! -f ${modulename}.xspice || \
+ ( -M ${modulename}.xspice < -M ${modulename}.spc )) then
echo "spi2xspice.py failure: No file ${modulename}.xspice created." \
|& tee -a ${synthlog}
endif
diff --git a/src/DEF2Verilog.c b/src/DEF2Verilog.c
new file mode 100644
index 0000000..f65d079
--- /dev/null
+++ b/src/DEF2Verilog.c
@@ -0,0 +1,482 @@
+//----------------------------------------------------------------
+// DEF2Verilog
+//----------------------------------------------------------------
+// Generate a structural verilog netlist from a DEF file. This is
+// used by qflow to get netlists after all modifications have been
+// made to the DEF file, including (but not necessarily limited to)
+// insertion of clock trees, insertion of fill, decap, and antenna
+// cells; and routing to antenna taps. The final DEF is presumed
+// to represent the expected post-routing netlist, so this netlist
+// is compared against the layout extracted netlist to check for
+// possible errors introduced by the router.
+//
+// Revision 0, 2018-12-1: First release by R. Timothy Edwards.
+//
+// This program is written in ISO C99.
+//----------------------------------------------------------------
+// Update 1/21/2019: Note that the DEF format does not preserve
+// verilog backslash escape names, so ensure that backslash names
+// include a space at the end of the name.
+//
+// Update 5/4/2019: Fixed handling of 2-dimensional arrays
+//----------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* For getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readverilog.h"
+#include "readlef.h"
+#include "readdef.h"
+
+void write_output(struct cellrec *top, char *vlogoutname);
+void helpmessage(FILE *outf);
+
+char *VddNet = NULL;
+char *GndNet = NULL;
+
+int nccount = 0; /* Count of global unconnected nets */
+
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, result;
+ unsigned char Flags;
+ float rscale;
+ struct cellrec *topcell;
+
+ char *definname = NULL;
+ char *vloginname = NULL;
+ char *vlogoutname = NULL;
+
+ while ((i = getopt(argc, argv, "hHv:o:l:p:g:")) != EOF) {
+ switch( i ) {
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ exit(0);
+ break;
+ case 'v':
+ vloginname = strdup(optarg);
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'p':
+ VddNet = strdup(optarg);
+ break;
+ case 'g':
+ GndNet = strdup(optarg);
+ break;
+ case 'o':
+ vlogoutname = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr,"Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ definname = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename for DEF input file.\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ if (vloginname)
+ topcell = ReadVerilog(vloginname);
+ else {
+ fprintf(stderr, "No verilog file specified (not yet handled).\n");
+ return 1;
+ }
+ result = DefRead(definname, &rscale);
+ write_output(topcell, vlogoutname);
+ return 0;
+}
+
+/* Structure to hold a pointer to a net record and an array bound pair */
+
+struct busData {
+ NET net;
+ int start;
+ int end;
+};
+
+/*----------------------------------------------------------------------*/
+/* Recursion callback function for each item in the NetTable hash */
+/*----------------------------------------------------------------------*/
+
+struct nlist *hash_nets(struct hashlist *p, void *cptr)
+{
+ struct hashtable *NetHash = (struct hashtable *)cptr;
+ NET net;
+ char *dptr, *sptr, *bptr;
+ struct busData *bdata;
+ int aidx;
+
+ /* Get gate from hash record */
+ net = (NET)(p->ptr);
+
+ /* Check for array delimiters. */
+ dptr = strrchr(net->netname, '[');
+ if (dptr != NULL) {
+ *dptr = '\0';
+ sscanf(dptr + 1, "%d", &aidx);
+ }
+ else aidx = -1;
+
+ /* Check for backslash-escape names modified by other tools */
+ /* (e.g., vlog2Cel) which replace the trailing space with a */
+ /* backslash, making the name verilog-incompatible. */
+
+ if (*net->netname == '\\') {
+ sptr = strchr(net->netname, ' ');
+ if (sptr == NULL) {
+ bptr = strrchr(net->netname + 1, '\\');
+ if (bptr != NULL) *bptr = ' ';
+ }
+ }
+
+ /* Check if record already exists */
+ bdata = HashLookup(net->netname, NetHash);
+ if (bdata == NULL) {
+ /* Allocate record */
+ bdata = (struct busData *)malloc(sizeof(struct busData));
+ bdata->start = bdata->end = aidx;
+ bdata->net = net;
+ HashPtrInstall(net->netname, bdata, NetHash);
+ }
+ else {
+ if (aidx != -1) {
+ if (aidx > bdata->start) bdata->start = aidx;
+ if (aidx < bdata->end) bdata->end = aidx;
+ }
+ }
+ if (dptr != NULL) *dptr = '[';
+ return NULL;
+}
+
+/*--------------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec nets hash table */
+/*--------------------------------------------------------------------------*/
+
+struct nlist *output_wires(struct hashlist *p, void *cptr)
+{
+ struct busData *bdata;
+ FILE *outf = (FILE *)cptr;
+
+ bdata = (struct busData *)(p->ptr);
+
+ fprintf(outf, "wire ");
+ if (bdata->start >= 0 && bdata->end >= 0) {
+ fprintf(outf, "[%d:%d] ", bdata->start, bdata->end);
+ }
+ fprintf(outf, "%s", p->name);
+
+ // Ensure backslash escaped names end in a space character per
+ // verilog syntax.
+ if (*(p->name) == '\\')
+ if (*(p->name + strlen(p->name) - 1) != ' ')
+ fprintf(outf, " ");
+
+ /* NOTE: The output format is fixed with power and ground */
+ /* specified as wires and set to binary values. May want */
+ /* additional command line options for various forms; otherwise, */
+ /* vlog2Verilog can translate between forms. */
+
+ if (VddNet && (!strcmp(p->name, VddNet)))
+ fprintf(outf, " = 1'b1");
+ else if (GndNet && (!strcmp(p->name, GndNet)))
+ fprintf(outf, " = 1'b0");
+
+ fprintf(outf, " ;\n");
+ return NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec properties */
+/* hash table */
+/*----------------------------------------------------------------------*/
+
+struct nlist *output_props(struct hashlist *p, void *cptr)
+{
+ char *propval = (char *)(p->ptr);
+ FILE *outf = (FILE *)cptr;
+
+ fprintf(outf, ".%s(%s),\n", p->name, propval);
+ return NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* Recursion callback function for each item in InstanceTable */
+/* Output all module instances. */
+/*----------------------------------------------------------------------*/
+
+struct nlist *output_instances(struct hashlist *p, void *cptr)
+{
+ GATE gate = (GATE)(p->ptr);
+ FILE *outf = (FILE *)cptr;
+ NODE node;
+ BUS bus;
+ int i, j, lastidx, nbus;
+ char ***net_array = NULL;
+
+ /* Ignore pins which are recorded as gates */
+ if (gate->gatetype == PinMacro) return NULL;
+
+ fprintf(outf, "%s ", gate->gatetype->gatename);
+ fprintf(outf, "%s (\n", gate->gatename);
+
+ /* In case power/ground pins are at the end of the list, find the */
+ /* index of the last valid output line. Consider any input pin */
+ /* that is not marked use POWER or GROUND to be automatically valid */
+ /* even if it is not connected (e.g., unconnected antenna cell */
+ /* input pins). */
+
+ for (lastidx = gate->nodes - 1; lastidx >= 0; lastidx--) {
+ node = gate->noderec[lastidx];
+ if (node) break;
+ else if ((gate->gatetype->direction[lastidx] == PORT_CLASS_INPUT)
+ && (gate->gatetype->use[lastidx] != PORT_USE_POWER)
+ && (gate->gatetype->use[lastidx] != PORT_USE_GROUND))
+ break;
+ }
+
+ /* If the gate defines pin buses, then prepare a set of arrays for */
+ /* each bus and its connections. */
+ if (gate->gatetype->bus != NULL) {
+ for (bus = gate->gatetype->bus, nbus = 0; bus; bus = bus->next) nbus++;
+ net_array = (char ***)malloc(nbus * sizeof(char **));
+ for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) {
+ net_array[i] = (char **)calloc((bus->high - bus->low + 1), sizeof(char *));
+ }
+ }
+
+ /* Write each port and net connection */
+ for (i = 0; i <= lastidx; i++) {
+ int is_nc_input;
+ char *netname;
+ node = gate->noderec[i];
+
+ /* node may be NULL if power or ground. Currently not handling */
+ /* this in expectation that the output is standard functional */
+ /* verilog without power and ground. Should have runtime */
+ /* options for handling power and ground in various ways. */
+ /* */
+ /* node may also be NULL if not connected, in which case it is */
+ /* optional to output it in verilog if it is an output. If it */
+ /* is an input, such as an unconnected antenna cell, then */
+ /* generate an arbitrary net name to represent it. */
+
+ is_nc_input = 0;
+ if ((node == NULL) &&
+ (gate->gatetype->direction[i] == PORT_CLASS_INPUT) &&
+ (gate->gatetype->use[i] != PORT_USE_POWER) &&
+ (gate->gatetype->use[i] != PORT_USE_GROUND)) {
+ is_nc_input = 1;
+ netname = (char *)malloc(32 * sizeof(char));
+ sprintf(netname, "_proxy_no_connect_%d_", nccount++);
+ }
+ else if (node) netname = node->netname;
+
+ if (node || is_nc_input) {
+ char *sptr, *eptr;
+ int k;
+
+ /* If the gate defines pin buses, then ignore individual */
+ /* pins and print out a single bus instead. */
+ if (gate->gatetype->bus != NULL) {
+ if ((sptr = strchr(gate->node[i], '[')) != NULL) {
+ if (sscanf(sptr + 1, "%d", &k) == 1) {
+ *sptr = '\0';
+ for (bus = gate->gatetype->bus, j = 0; bus;
+ bus = bus->next, j++) {
+ if (!strcmp(bus->busname, gate->node[i])) {
+ net_array[j][k] = strdup(netname);
+ break;
+ }
+ }
+ *sptr = '[';
+ if (bus != NULL) continue;
+ }
+ }
+ }
+
+ fprintf(outf, " .%s(%s)", gate->node[i], netname);
+ if ((i != lastidx) || (gate->bus != NULL)) fprintf(outf, ",");
+ fprintf(outf, "\n");
+ }
+ if (is_nc_input) free(netname);
+ }
+
+ /* Write out bus connections */
+ for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) {
+ int defnets = 0, samenet = 0;
+ char *dptr, *d0ptr = NULL;
+
+ fprintf(outf, " .%s(", bus->busname);
+ for (j = bus->low; j <= bus->high; j++) {
+ if (net_array[i][j] != NULL) {
+ defnets++;
+ if ((dptr = strrchr(net_array[i][j], '[')) != NULL) {
+ *dptr = '\0';
+ if (j == bus->low) {
+ d0ptr = dptr;
+ }
+ else {
+ if (!strcmp(net_array[i][j], net_array[i][bus->low])) samenet++;
+ *dptr = '[';
+ }
+ }
+ }
+ }
+ if (d0ptr != NULL) *d0ptr = '[';
+ if ((d0ptr != NULL) && (samenet == (bus->high - bus->low))) {
+ *d0ptr = '\0';
+ fprintf(outf, "%s", net_array[i][bus->low]);
+ *d0ptr = '[';
+ }
+ else if (defnets > 0) {
+ fprintf(outf, "{");
+ for (j = bus->high; j >= bus->low; j--) {
+ if (net_array[i][j] != NULL) {
+ fprintf(outf, "%s", net_array[i][j]);
+ }
+ if (j > bus->low) fprintf(outf, ",");
+ }
+ fprintf(outf, "}");
+ }
+ fprintf(outf, ")");
+ if (bus->next != NULL) fprintf(outf, ",");
+ fprintf(outf, "\n");
+ }
+
+ fprintf(outf, ");\n\n");
+
+ if (gate->gatetype->bus != NULL) {
+ /* Free memory allocated to net arrays */
+ for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) {
+ for (j = bus->low; j <= bus->high; j++)
+ if (net_array[i][j]) free(net_array[i][j]);
+ free(net_array[i]);
+ }
+ free(net_array);
+ }
+
+ return NULL;
+}
+
+/*--------------------------------------------------------------*/
+/* write_output */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void write_output(struct cellrec *topcell, char *vlogoutname)
+{
+ FILE *outfptr = stdout;
+
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+ GATE gate;
+
+ struct hashtable NetHash;
+
+ if (vlogoutname != NULL) {
+ outfptr = fopen(vlogoutname, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Failed to open file %s for writing netlist output\n",
+ vlogoutname);
+ return;
+ }
+ }
+
+ InitializeHashTable(&NetHash, LARGEHASHSIZE);
+
+ /* Write output module header */
+ fprintf(outfptr, "/* Verilog module written by DEF2Verilog (qflow) */\n");
+ fprintf(outfptr, "module %s (\n", topcell->name);
+
+ /* Output the verilog netlist verbatim through the list of ports. */
+
+ for (port = topcell->portlist; port; port = port->next) {
+ if (port->name == NULL) continue;
+ switch(port->direction) {
+ case PORT_INPUT:
+ fprintf(outfptr, " input ");
+ break;
+ case PORT_OUTPUT:
+ fprintf(outfptr, " output ");
+ break;
+ case PORT_INOUT:
+ fprintf(outfptr, " inout ");
+ break;
+ }
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ fprintf(outfptr, "[%d:%d] ", net->start, net->end);
+ }
+ fprintf(outfptr, "%s", port->name);
+ if (port->next) fprintf(outfptr, ",");
+ fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, ");\n\n");
+
+ /* Find all wires and create a hash table */
+ /* (To do: cross check against topcell->nets to ensure correct array bounds) */
+
+ RecurseHashTablePointer(&NetTable, hash_nets, &NetHash);
+ RecurseHashTablePointer(&NetHash, output_wires, outfptr);
+ fprintf(outfptr, "\n");
+
+ /* Write instances in the order found in the DEF file */
+ RecurseHashTablePointer(&InstanceTable, output_instances, outfptr);
+
+ /* End the module */
+ fprintf(outfptr, "endmodule\n");
+
+ if (vlogoutname != NULL) fclose(outfptr);
+
+ fflush(stdout);
+}
+
+/*--------------------------------------------------------------*/
+/* C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "DEF2Verilog [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "DEF2Verilog converts a DEF file to a verilog structural\n");
+ fprintf(outf, "netlist. Output on stdout.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, " -v <path> Path to verilog file (for I/O list)\n");
+ fprintf(outf, " -l <path> Path to standard cell LEF file (for macro list)\n");
+ fprintf(outf, " -p <name> Name of power net\n");
+ fprintf(outf, " -g <name> Name of ground net\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -h Print this message\n");
+
+} /* helpmessage() */
+
diff --git a/src/Makefile.in b/src/Makefile.in
index 501bdf9..8d1b8ff 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -12,26 +12,34 @@ DEFS = @DEFS@ -DQFLOW_VERSION=\"@VERSION@\" -DQFLOW_REVISION=\"@REVISION@\"
QFLOW_LIB_DIR = /usr/lib/qflow
QFLOW_GRAYWOLF_PATH = @QFLOW_GRAYWOLF_PATH@
+QFLOW_REPLACE_PATH = @QFLOW_REPLACE_PATH@
+QFLOW_NTUPLACE3_PATH = @QFLOW_NTUPLACE3_PATH@
+QFLOW_NTUPLACE4_PATH = @QFLOW_NTUPLACE4_PATH@
QFLOW_QROUTER_PATH = @QFLOW_QROUTER_PATH@
-QFLOW_ABC_PATH = @QFLOW_ABC_PATH@
-QFLOW_ODIN_PATH = @QFLOW_ODIN_PATH@
QFLOW_MAGIC_PATH = @QFLOW_MAGIC_PATH@
QFLOW_NETGEN_PATH = @QFLOW_NETGEN_PATH@
QFLOW_YOSYS_PATH = @QFLOW_YOSYS_PATH@
QFLOW_OPENTIMER_PATH = @QFLOW_OPENTIMER_PATH@
QFLOW_OPENSTA_PATH = @QFLOW_OPENSTA_PATH@
-HAVE_ABC = @HAVE_ABC@
HAVE_YOSYS = @HAVE_YOSYS@
-HAVE_ODIN_II = @HAVE_ODIN_II@
HAVE_MAGIC = @HAVE_MAGIC@
HAVE_NETGEN = @HAVE_NETGEN@
HAVE_OPENTIMER = @HAVE_OPENTIMER@
HAVE_OPENSTA = @HAVE_OPENSTA@
-
-OBJECTS = blif2BSpice.o blif2Verilog.o blifFanout.o vesta.o spice2delay.o rc2dly.o
+HAVE_GRAYWOLF = @HAVE_GRAYWOLF@
+HAVE_REPLACE = @HAVE_REPLACE@
+HAVE_QROUTER = @HAVE_QROUTER@
+
+OBJECTS = vlog2Spice.o vlog2Verilog.o vlog2Def.o vlog2Cel.o vlogFanout.o
+OBJECTS += DEF2Verilog.o addspacers.o
+OBJECTS += vesta.o spice2delay.o rc2dly.o
+OBJECTS += blif2BSpice.o blif2Verilog.o blifFanout.o
HASHLIB = hash.o
LIBERTYLIB = readliberty.o
+VERILOGLIB = readverilog.o
+LEFLIB = readlef.o
+DEFLIB = readdef.o
SOURCES := $(patsubst %.o,%.c,$(OBJECTS))
TARGETS := $(patsubst %.o,%$(EXEEXT),$(OBJECTS))
@@ -39,40 +47,73 @@ BININSTALL = ${QFLOW_LIB_DIR}/bin
all: $(TARGETS)
+vlog2Spice$(EXEEXT): vlog2Spice.o $(HASHLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vlog2Spice.o $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS)
+
+vlog2Verilog$(EXEEXT): vlog2Verilog.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vlog2Verilog.o $(HASHLIB) $(VERILOGLIB) $(LEFLIB) \
+ -o $@ $(LIBS)
+
+vlog2Cel$(EXEEXT): vlog2Cel.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vlog2Cel.o $(HASHLIB) $(VERILOGLIB) $(LEFLIB) \
+ -o $@ $(LIBS)
+
+vlog2Def$(EXEEXT): vlog2Def.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vlog2Def.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB) -o $@ $(LIBS) -lm
+
+vlogFanout$(EXEEXT): vlogFanout.o $(HASHLIB) $(LIBERTYLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vlogFanout.o $(HASHLIB) $(VERILOGLIB) $(LIBERTYLIB) \
+ -DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \
+ $(DEPENDS) -o $@ $(LIBS) -lm
+
+DEF2Verilog$(EXEEXT): DEF2Verilog.o $(HASHLIB) $(VERILOGLIB) $(DEFLIB) $(LEFLIB)
+ $(CC) $(LDFLAGS) DEF2Verilog.o $(HASHLIB) $(VERILOGLIB) $(DEFLIB) $(LEFLIB) \
+ -DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \
+ $(DEPENDS) -o $@ $(LIBS) -lm
+
+addspacers$(EXEEXT): addspacers.o $(HASHLIB) $(LEFLIB) $(DEFLIB)
+ $(CC) $(LDFLAGS) addspacers.o $(HASHLIB) $(LEFLIB) $(DEFLIB) -o $@ $(LIBS) -lm
+
blif2BSpice$(EXEEXT): blif2BSpice.o
$(CC) $(LDFLAGS) blif2BSpice.o -o $@ $(LIBS)
+blif2Verilog$(EXEEXT): blif2Verilog.o
+ $(CC) $(LDFLAGS) blif2Verilog.o -o $@ $(LIBS)
+
blifFanout$(EXEEXT): blifFanout.o $(HASHLIB) $(LIBERTYLIB)
$(CC) $(LDFLAGS) blifFanout.o $(HASHLIB) $(LIBERTYLIB) \
-DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \
$(DEPENDS) -o $@ $(LIBS) -lm
-blif2Verilog$(EXEEXT): blif2Verilog.o
- $(CC) $(LDFLAGS) blif2Verilog.o -o $@ $(LIBS)
-
-vesta$(EXEEXT): vesta.o $(HASHLIB)
- $(CC) $(LDFLAGS) vesta.o $(HASHLIB) -o $@ $(LIBS)
+vesta$(EXEEXT): vesta.o $(HASHLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) vesta.o $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS)
spice2delay$(EXEEXT): spice2delay.o $(HASHLIB) $(LIBERTYLIB)
$(CC) $(LDFLAGS) spice2delay.o $(HASHLIB) $(LIBERTYLIB) -o $@ $(LIBS)
-rc2dly$(EXEEXT): rc2dly.o $(LIBERTYLIB)
- $(CC) $(LDFLAGS) rc2dly.o $(LIBERTYLIB) -o $@ $(LIBS)
+rc2dly$(EXEEXT): rc2dly.o $(LIBERTYLIB) $(HASHLIB) $(VERILOGLIB)
+ $(CC) $(LDFLAGS) rc2dly.o $(LIBERTYLIB) $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS)
install: $(TARGETS)
- @echo "Installing verilog and BDNET file format handlers"
+ @echo "Installing verilog, SPICE, etc. file format handlers"
$(INSTALL) -d $(DESTDIR)${BININSTALL}
@for target in $(TARGETS); do \
$(INSTALL) $$target $(DESTDIR)${BININSTALL} ;\
done
- @echo "Installing links to graywolf, qrouter, odin_ii, and abc"
- (cd $(DESTDIR)${BININSTALL}; $(RM) -f graywolf; ln -s $(QFLOW_GRAYWOLF_PATH) graywolf)
- (cd $(DESTDIR)${BININSTALL}; $(RM) -f qrouter; ln -s $(QFLOW_QROUTER_PATH) qrouter)
- @if test "${HAVE_ABC}" = "1"; then \
- (cd $(DESTDIR)${BININSTALL}; $(RM) -f abc; ln -s $(QFLOW_ABC_PATH) abc); \
+ @echo "Installing links to third-party synthesis flow tool executables"
+ @if test "${HAVE_GRAYWOLF}" = "1"; then \
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f graywolf; ln -s $(QFLOW_GRAYWOLF_PATH) graywolf) ;\
+ fi
+ @if test "${HAVE_REPLACE}" = "1"; then \
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f RePlAce; ln -s $(QFLOW_REPLACE_PATH) RePlAce) ;\
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f ntuplace3; ln -s $(QFLOW_NTUPLACE3_PATH) ntuplace3) ;\
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f ntuplace4h; ln -s $(QFLOW_NTUPLACE4_PATH) ntuplace4h) ;\
+ fi
+ @if test "${HAVE_QROUTER}" = "1"; then \
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f qrouter; ln -s $(QFLOW_QROUTER_PATH) qrouter) ;\
fi
@if test "${HAVE_MAGIC}" = "1"; then \
- (cd $(DESTDIR)${BININSTALL}; $(RM) -f magic; ln -s $(QFLOW_MAGIC_PATH) magic); \
+ (cd $(DESTDIR)${BININSTALL}; $(RM) -f magic; ln -s $(QFLOW_MAGIC_PATH) magic) ;\
fi
@if test "${HAVE_NETGEN}" = "1"; then \
(cd $(DESTDIR)${BININSTALL}; $(RM) -f netgen; ln -s $(QFLOW_NETGEN_PATH) netgen); \
@@ -92,15 +133,15 @@ uninstall:
$(RM) -rf ${BININSTALL}
clean:
- $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB)
+ $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB)
$(RM) -f $(TARGETS)
veryclean:
- $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB)
+ $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB)
$(RM) -f $(TARGETS)
distclean:
- $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB)
+ $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB)
$(RM) -f $(TARGETS)
.c.o:
diff --git a/src/addspacers.c b/src/addspacers.c
new file mode 100644
index 0000000..b0d211d
--- /dev/null
+++ b/src/addspacers.c
@@ -0,0 +1,2026 @@
+/*------------------------------------------------------*/
+/* addspacers --- */
+/* */
+/* Tool for adding fill cells and other post-placement */
+/* cells and data into a DEF format layout. */
+/* */
+/* Primary functions are: */
+/* */
+/* 1) Fill the core area out to the bounding box with */
+/* fill cells to make the edges straight and fill */
+/* any gaps. */
+/* */
+/* 2) Create power stripes and power posts, adding */
+/* additional fill under (or near, as available) */
+/* the power stripes if requested. */
+/* */
+/* 3) Adjust pin positions to match any stretching of */
+/* the cell done in (2) */
+/* */
+/* 4) Adjust obstruction layers to match any */
+/* stretching of the cell done in (2) */
+/* */
+/* 5) Write the modified DEF layout */
+/* */
+/* 6) Write the modified obstruction file (if */
+/* modified). */
+/* */
+/*------------------------------------------------------*/
+/* */
+/* This file previously addspacers.tcl until it became */
+/* painfully obvious that the amount of calculation */
+/* involved made it a very poor choice to be done by */
+/* an interpreter. */
+/* */
+/*------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* For getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readlef.h"
+#include "readdef.h"
+
+/* Flags fields */
+
+#define NOSTRETCH 1
+#define OBSTRUCT 2
+#define VERBOSE 4
+#define FILLWARNED 8
+
+/* Structure used to contain core area bounding box. Also records */
+/* core site height and width. */
+
+typedef struct _corebbox *COREBBOX;
+
+typedef struct _corebbox {
+ int llx; /* Layout bounds */
+ int lly;
+ int urx;
+ int ury;
+ int urx_exp; /* Expanded core urx */
+ int sitew; /* Core site width */
+ int siteh; /* Core site height */
+ int fillmin; /* Minimum fill cell width */
+ int orient; /* Orientation of the first (lowest) row */
+} corebbox;
+
+/* Structure used to hold the final calculated pitch and width of stripes */
+
+typedef struct _stripeinfo *SINFO;
+
+typedef struct _stripeinfo {
+ int width;
+ int pitch;
+ int offset;
+ int stretch; /* Maximum amount of layout stretching */
+ int number; /* Total number of stripes */
+} stripeinfo;
+
+/* List of gates (used to sort fill cells) */
+typedef struct filllist_ *FILLLIST;
+
+struct filllist_ {
+ FILLLIST next;
+ FILLLIST last;
+ GATE gate;
+ int width;
+};
+
+/* Structures for defining a power stripe. */
+
+typedef struct _powerpost *PPOST;
+
+typedef struct _powerpost {
+ PPOST next;
+ DSEG strut;
+ LefList viagen;
+} powerpost;
+
+typedef struct _powerstripe *PSTRIPE;
+
+typedef struct _powerstripe {
+ PSTRIPE next; // One stripe definition centered on X = 0
+ PPOST posts; // All posts for one stripe, centered on X = 0
+ DSEG stripe;
+ int offset; // Horizontal start of posts
+ int num; // Number of stripes
+ int pitch; // Spacing between stripes
+ char *name; // Net name of power rail
+} powerstripe;
+
+/* Hash table of instances hashed by (X, Y) position */
+struct hashtable CellPosTable;
+
+/* Forward declarations */
+unsigned char check_overcell_capable(unsigned char Flags);
+FILLLIST generate_fill(char *fillcellname, float rscale, COREBBOX corearea,
+ unsigned char Flags);
+SINFO generate_stripefill(char *VddNet, char *GndNet,
+ char *stripepat, float stripewidth_t, float stripepitch_t,
+ char *fillcellname, float scale, FILLLIST fillcells,
+ COREBBOX corearea, unsigned char Flags);
+void fix_obstructions(char *definname, SINFO stripevals, float scale,
+ unsigned char Flags);
+PSTRIPE generate_stripes(SINFO stripevals, FILLLIST fillcells,
+ COREBBOX corearea, char *stripepat, char *VddNet, char *GndNet,
+ float scale, unsigned char Flags);
+void write_output(char *definname, char *defoutname, float scale,
+ COREBBOX corearea, SINFO stripevals, PSTRIPE rails,
+ char *VddNet, char *GndNet, unsigned char Flags);
+void helpmessage(FILE *outf);
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, result;
+ unsigned char Flags;
+ float rscale;
+ struct cellrec *topcell;
+ COREBBOX corearea;
+ FILLLIST fillcells;
+ SINFO stripevals;
+ PSTRIPE rails;
+
+ char *definname = NULL;
+ char *fillcellname = NULL;
+ char *defoutname = NULL;
+
+ float stripewidth_t, stripepitch_t;
+ static char* default_stripepat = "PG";
+ char *stripepat = default_stripepat;
+
+ char *VddNet = NULL;
+ char *GndNet = NULL;
+
+ Flags = 0;
+ stripewidth_t = stripepitch_t = 0.0;
+
+ while ((i = getopt(argc, argv, "hHvOn:o:l:p:g:f:w:P:s:")) != EOF) {
+ switch( i ) {
+ case 'v':
+ Flags |= VERBOSE;
+ /* Also set global variable used by readlef.c */
+ Verbose = 1;
+ break;
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ exit(0);
+ break;
+ case 'n':
+ Flags |= NOSTRETCH;
+ break;
+ case 'O':
+ Flags |= OBSTRUCT;
+ break;
+ case 'w':
+ if (sscanf(optarg, "%g", &stripewidth_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg);
+ }
+ break;
+ case 'P':
+ if (sscanf(optarg, "%g", &stripepitch_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg);
+ }
+ break;
+ case 'o':
+ defoutname = strdup(optarg);
+ break;
+ case 'f':
+ fillcellname = strdup(optarg);
+ break;
+ case 's':
+ if (!strcmp(optarg, "tripe")) {
+ /* Accept the argument "-stripe <width> <pitch> <pattern>" */
+ /* that was the option for the old addspacers.tcl script. */
+ /* Combines width, pitch, and pattern in one argument. */
+ if (sscanf(argv[optind], "%g", &stripewidth_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]);
+ }
+ optind++;
+ if (sscanf(argv[optind], "%g", &stripepitch_t) != 1) {
+ fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]);
+ }
+ optind++;
+ stripepat = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ stripepat = strdup(optarg);
+ }
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'p':
+ VddNet = strdup(optarg);
+ break;
+ case 'g':
+ GndNet = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr,"Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ char *pptr;
+
+ definname = (char *)malloc(strlen(argv[optind]) + 5);
+ strcpy(definname, argv[optind]);
+ pptr = strrchr(argv[optind], '.');
+ if (pptr == NULL) strcat(definname, ".def");
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename for DEF input file.\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ result = DefRead(definname, &rscale);
+
+ corearea = (COREBBOX)malloc(sizeof(corebbox));
+
+ Flags |= check_overcell_capable(Flags);
+ fillcells = generate_fill(fillcellname, rscale, corearea, Flags);
+ if (fillcells == NULL) {
+ fprintf(stderr, "Failed to parse any fill cells from the standard cell library.\n");
+ return 1;
+ }
+ stripevals = generate_stripefill(VddNet, GndNet, stripepat,
+ stripewidth_t, stripepitch_t, fillcellname, rscale, fillcells,
+ corearea, Flags);
+ fix_obstructions(definname, stripevals, rscale, Flags);
+ rails = generate_stripes(stripevals, fillcells, corearea, stripepat,
+ VddNet, GndNet, rscale, Flags);
+ write_output(definname, defoutname, rscale, corearea, stripevals,
+ rails, VddNet, GndNet, Flags);
+
+ return 0;
+}
+
+/*--------------------------------------------------------------*/
+/* Find empty spaces in the DEF layout and insert fill macros */
+/*--------------------------------------------------------------*/
+
+FILLLIST
+generate_fill(char *fillcellname, float scale, COREBBOX corearea, unsigned char Flags)
+{
+ GATE gate, newfillinst;
+ ROW row;
+ int isfill, orient;
+ int corew = 1, coreh = 0, testh;
+ int instx, insty, instw, insth, fnamelen;
+ int x, y, dx, nx, fillmin;
+ char posname[32];
+
+ int corellx = 0;
+ int corelly = 0;
+ int coreurx = 0;
+ int coreury = 0;
+
+ FILLLIST fillcells = NULL;
+ FILLLIST newfill, testfill;
+
+ /* Parse library macros and find CORE SITE definition */
+
+ for (gate = GateInfo; gate; gate = gate->next)
+ {
+ if (!strncmp(gate->gatename, "site_", 5)) {
+ if (gate->gateclass == MACRO_CLASS_CORE) {
+ corew = (int)(roundf(gate->width * scale));
+ testh = (int)(roundf(gate->height * scale));
+ // Sometimes there are multiple-height core sites. . .
+ if (coreh == 0 || (testh < coreh)) coreh = testh;
+ }
+ }
+ }
+ if (corew == 0) {
+ fprintf(stderr, "Warning: failed to find any core site.\n");
+ /* Use route pitch for step size */
+ corew = (int)(roundf(LefGetRoutePitch(0) * scale));
+ }
+ if (corew == 0) {
+ fprintf(stderr, "Error: failed to find any core site or route pitch.\n");
+ return NULL;
+ }
+
+ /* Parse library macros and find fill cells. Use "fillcellname" as a */
+ /* prefix, if given; if not, find spacer cells by class. */
+
+ fnamelen = (fillcellname) ? strlen(fillcellname) : 0;
+
+ for (gate = GateInfo; gate; gate = gate->next)
+ {
+ isfill = FALSE;
+ if (fillcellname) {
+ if (!strncmp(gate->gatename, fillcellname, fnamelen))
+ isfill = TRUE;
+ }
+ else if (gate->gatesubclass == MACRO_SUBCLASS_SPACER)
+ isfill = TRUE;
+
+ if (isfill == TRUE) {
+ newfill = (FILLLIST)malloc(sizeof(struct filllist_));
+ newfill->gate = gate;
+ newfill->width = (int)(roundf(gate->width * scale));
+
+ /* Insert in fill list in width order, high to low */
+ if (fillcells == NULL) {
+ newfill->next = newfill->last = NULL;
+ fillcells = newfill;
+ }
+ else {
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width < newfill->width) {
+ /* insert before */
+ newfill->next = testfill;
+ newfill->last = testfill->last;
+ if (testfill->last == NULL)
+ fillcells = newfill;
+ else
+ testfill->last->next = newfill;
+ testfill->last = newfill;
+ break;
+ }
+ else if (testfill->next == NULL) {
+ /* put at end */
+ newfill->next = NULL;
+ testfill->next = newfill;
+ newfill->last = testfill;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (fillcells == NULL) {
+ fprintf(stderr, "Error: No fill cells have been specified or found.\n");
+ return NULL;
+ }
+
+ if (fillcells) {
+ testh = (int)(roundf(fillcells->gate->height * scale));
+ if ((coreh == 0) || (coreh < testh)) {
+ /* Use fill cell height for core height */
+ coreh = testh;
+ }
+ }
+ if (coreh == 0) {
+ fprintf(stderr, "Error: failed to find any core site or standard cell height.\n");
+ return NULL;
+ }
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Core site is %g x %g um\n",
+ (float)corew / scale, (float)coreh / scale);
+
+
+ /* Rehash all instances by position */
+ /* Find minimum and maximum bounds, and record cell in lower left position */
+
+ InitializeHashTable(&CellPosTable, LARGEHASHSIZE);
+
+ for (gate = Nlgates; gate; gate = gate->next)
+ {
+ /* Do not evaluate pins, only core cells */
+ if (gate->gatetype == NULL) continue;
+
+ instx = (int)(roundf(gate->placedX * scale));
+ insty = (int)(roundf(gate->placedY * scale));
+ instw = (int)(roundf(gate->width * scale));
+ insth = (int)(roundf(gate->height * scale));
+ sprintf(posname, "%dx%d", instx, insty);
+ HashPtrInstall(posname, gate, &CellPosTable);
+
+ if (corellx == coreurx) {
+ corellx = instx;
+ coreurx = instx + instw;
+ corelly = insty;
+ coreury = insty + insth;
+ }
+ else {
+ if (instx < corellx) corellx = instx;
+ else if (instx + instw > coreurx) coreurx = instx + instw;
+
+ if (insty < corelly) corelly = insty;
+ else if (insty + insth > coreury) coreury = insty + insth;
+ }
+ }
+
+ fprintf(stdout, "Initial core layout: (%d %d) to (%d %d) (scale um * %d)\n",
+ corellx, corelly, coreurx, coreury, (int)scale);
+
+ if (Flags & VERBOSE) fprintf(stdout, "Adding fill cells.\n");
+
+ /* Find the orientation of the first row and record this */
+ /* in corearea. NOTE: This is simpler if the DEF file records */
+ /* ROWs, something that needs to be done in place2def. */
+
+ row = DefFindRow(corelly);
+ if (row) {
+ corearea->orient = row->orient & (RN | RS);
+ }
+ else {
+ corearea->orient = RN;
+ y = corelly;
+ x = corellx;
+ while (x < coreurx) {
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate != NULL) {
+ corearea->orient = gate->orient & (RN | RS);
+ break;
+ }
+ x += testfill->width;
+ }
+ }
+ orient = corearea->orient;
+
+ /* Starting from the lower-left corner, find gate at each site */
+ /* position. If there is no gate at the site position, then */
+ /* find the next gate forward, and add fill. */
+
+ /* NOTE: This routine does not account for obstruction areas */
+ /* used to define a non-rectangular core area (to be done) */
+
+ for (y = corelly; y < coreury; y += coreh) {
+ x = corellx;
+ while (x < coreurx) {
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate == NULL) {
+ for (nx = x + corew; nx < coreurx; nx += corew) {
+ sprintf(posname, "%dx%d", nx, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ if (gate != NULL) break;
+ }
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Add fill from (%d %d) to (%d %d)\n",
+ x, y, nx, y);
+ dx = nx - x;
+
+ while (dx > 0) {
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width <= dx) break;
+ }
+ if (testfill == NULL) {
+ if (nx == coreurx) {
+ if (!(Flags & FILLWARNED)) {
+ fprintf(stderr, "Notice: Right edge of layout"
+ " cannot be cleanly aligned due to\n");
+ fprintf(stderr, "limited fill cell widths.\n");
+ }
+ Flags |= FILLWARNED; // Do not repeat this message
+ x = nx;
+ dx = 0;
+ break;
+ }
+ else {
+ fprintf(stderr, "Error: Empty slot at (%g, %g) is smaller"
+ " than any available fill cell.\n",
+ (float)x / scale, (float)y / scale);
+ }
+ }
+
+ /* Create new fill instance */
+ newfillinst = (GATE)malloc(sizeof(struct gate_));
+ if (testfill)
+ newfillinst->gatetype = testfill->gate;
+ else
+ newfillinst->gatetype = NULL; /* placeholder */
+ sprintf(posname, "FILL%dx%d", x, y);
+ newfillinst->gatename = strdup(posname);
+ newfillinst->placedX = (double)x / (double)scale;
+ newfillinst->placedY = (double)y / (double)scale;
+ newfillinst->clientdata = (void *)NULL;
+ row = DefFindRow(y);
+ newfillinst->orient = (row) ? row->orient : orient;
+ DefAddGateInstance(newfillinst);
+
+ /* Hash the new instance position */
+ sprintf(posname, "%dx%d", x, y);
+ HashPtrInstall(posname, newfillinst, &CellPosTable);
+
+ if (testfill) {
+ x += testfill->width;
+ dx -= testfill->width;
+ }
+ else {
+ newfillinst->width = dx;
+ x += dx;
+ dx = 0;
+ }
+ }
+ }
+ else {
+ x += (int)(roundf(gate->width * scale));
+ }
+ }
+ /* Flip orientation each row (NOTE: This is not needed if ROW */
+ /* statements are in the DEF file! */
+ orient = (orient == RN) ? RS : RN;
+ }
+
+ if (fillcells) {
+ for (testfill = fillcells; testfill->next; testfill = testfill->next);
+ fillmin = testfill->width;
+ }
+ else fillmin = 0;
+
+ corearea->llx = corellx;
+ corearea->lly = corelly;
+ corearea->urx = coreurx;
+ corearea->urx_exp = coreurx;
+ corearea->ury = coreury;
+ corearea->sitew = corew;
+ corearea->siteh = coreh;
+ corearea->fillmin = fillmin;
+
+ return fillcells;
+}
+
+/*--------------------------------------------------------------*/
+/* Check if there are not enough metal layers to route power */
+/* over the cell. If not, then force the NOSTRETCH option. */
+/*--------------------------------------------------------------*/
+
+unsigned char check_overcell_capable(unsigned char Flags)
+{
+ int ltop;
+
+ if (!(Flags & NOSTRETCH)) {
+ ltop = LefGetMaxRouteLayer() - 1;
+ if (LefGetRouteOrientation(ltop) == 1) ltop--;
+ if (ltop < 3) {
+ fprintf(stderr, "Warning: Stretching requested, but not applicable.\n");
+ return (unsigned char)NOSTRETCH;
+ }
+ }
+ return (unsigned char)0;
+}
+
+/*--------------------------------------------------------------*/
+/* Generate power stripes */
+/*--------------------------------------------------------------*/
+
+SINFO
+generate_stripefill(char *VddNet, char *GndNet, char *stripepat,
+ float stripewidth_t, float stripepitch_t,
+ char *fillcellname, float scale, FILLLIST fillcells,
+ COREBBOX corearea, unsigned char Flags)
+{
+ int numstripes;
+ int minstripes;
+ int corew, tw, tp, tr, dx, x, y, nx;
+ int stripepitch_i, stripewidth_i, stripeoffset_i;
+ int stripepitch_f, stripewidth_f, stripeoffset_f;
+ int totalw;
+ int orient;
+ int nextx, totalfx;
+ FILLLIST fillseries, testfill, newfill, sfill;
+ SINFO stripevals;
+ char posname[32];
+ GATE gate, newfillinst;
+ ROW row;
+
+ stripevals = (SINFO)malloc(sizeof(stripeinfo));
+ stripevals->pitch = 0;
+ stripevals->width = 0;
+ stripevals->offset = 0;
+
+ corew = corearea->urx - corearea->llx;
+
+ if (stripewidth_t <= 0.0 || stripepitch_t <= 0.0) {
+ fprintf(stdout, "No stripe information provided; no power stripes added.\n");
+ return stripevals;
+ }
+
+ minstripes = strlen(stripepat);
+
+ /* Scale stripe width and pitch from microns to DEF database units */
+ stripewidth_i = (int)(roundf(stripewidth_t * scale));
+ stripepitch_i = (int)(roundf(stripepitch_t * scale));
+
+ /* Adjust stripewidth to the nearest core site unit width. */
+ /* If stretching is specified and the minimum fill cell width */
+ /* is greater than the core site width, then adjust to the */
+ /* nearest multiple of the minimum fill cell width. */
+
+ if ((!(Flags & NOSTRETCH)) && (corearea->fillmin > corearea->sitew)) {
+ tw = stripewidth_i / corearea->fillmin;
+ tr = stripewidth_i % corearea->fillmin;
+ stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->fillmin;
+ }
+ else {
+ tw = stripewidth_i / corearea->sitew;
+ tr = stripewidth_i % corearea->sitew;
+ stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->sitew;
+ }
+
+ if (!(Flags & NOSTRETCH))
+ {
+ /* Find a series of fill cell macros to match the stripe width */
+ fillseries = NULL;
+ dx = stripewidth_f;
+ while (dx > 0) {
+ int diff;
+
+ for (testfill = fillcells; testfill; testfill = testfill->next) {
+ if (testfill->width <= dx) break;
+ }
+ if (testfill == NULL) {
+ /* This can happen if there is no fill cell that is the */
+ /* same width as the minimum site pitch. If so, find */
+ /* the first non-minimum-size fill cell and change it */
+ /* to minimum size, then continue. */
+ for (testfill = fillcells; testfill && testfill->next;
+ testfill = testfill->next);
+ for (sfill = fillseries; sfill; sfill = sfill->next)
+ if (sfill->gate != testfill->gate) break;
+ if (sfill == NULL) {
+ fprintf(stderr, "Warning: failed to find fill cell series matching"
+ " the requested stripe width.\n");
+ fprintf(stderr, "Stripe width will be modified as needed.\n");
+ dx = 0;
+ break;
+ }
+ diff = sfill->width - testfill->width;
+ sfill->gate = testfill->gate;
+ sfill->width = testfill->width;
+ dx += diff;
+ }
+ newfill = (FILLLIST)malloc(sizeof(struct filllist_));
+ newfill->gate = testfill->gate;
+ newfill->width = testfill->width;
+ newfill->next = fillseries;
+ fillseries = newfill;
+ dx -= newfill->width;
+ }
+
+ /* In case the fill series width does not equal the expected */
+ /* stripe width, set the stripe width to the fill series width. */
+
+ stripewidth_f = 0;
+ for (sfill = fillseries; sfill; sfill = sfill->next)
+ stripewidth_f += sfill->width;
+ }
+
+ /* Adjust stripepitch to the nearest core site unit width */
+ tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ if (stripepitch_f < stripewidth_f * 2) {
+ fprintf(stderr, "Error: Stripe pitch is too small (pitch = %g, width = %g)!\n",
+ (float)stripepitch_f / (float)scale,
+ (float)stripewidth_f / (float)scale);
+ return stripevals;
+ }
+ if ((fillcells == NULL) && (!(Flags & NOSTRETCH))) {
+ fprintf(stderr, "No fill cells defined. Not stretching layout.\n");
+ Flags |= NOSTRETCH;
+ }
+
+ if (stripepitch_f != stripepitch_i) {
+ fprintf(stderr, "Stripe pitch requested = %g, stripe pitch used = %g\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ if (stripewidth_f != stripewidth_i) {
+ fprintf(stderr, "Stripe width requested = %g, stripe width used = %g\n",
+ stripewidth_t, (float)stripewidth_f / (float)scale);
+ }
+
+ if (!(Flags & NOSTRETCH))
+ {
+ /* Cell will be stretched, so compute the amount to be */
+ /* streteched per stripe, which is the stripewidth */
+ /* adjusted to the nearest unit site width. */
+
+ numstripes = corew / (stripepitch_f - stripewidth_f);
+
+ if (numstripes < minstripes) {
+ numstripes = minstripes;
+
+ /* Recompute stripe pitch */
+ stripepitch_f = corew / numstripes;
+ tp = (int)(0.5 + (float)stripepitch_f / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ totalw = corew + numstripes * stripewidth_f;
+
+ /* Find offset to center of 1st power stripe that results in */
+ /* centering the stripes on the layout. */
+
+ stripeoffset_i = (totalw - (numstripes - 1) * stripepitch_f) / 2;
+ tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew);
+ stripeoffset_f = (tp * corearea->sitew);
+
+ /* Add fill cells (approximately) under stripe positions. */
+ /* Note that this is independent of drawing the stripes and */
+ /* power posts. There is no requirement that the stretch fill */
+ /* must be directly under the stripe, only that the total cell */
+ /* width is increased by the total width of all stripes, and */
+ /* the extra fill is added as close to each stripe as possible. */
+
+ orient = corearea->orient;
+ for (y = corearea->lly; y < corearea->ury; y += corearea->siteh) {
+ nextx = corearea->llx + stripeoffset_f - stripewidth_f / 2;
+ totalfx = 0;
+
+ x = corearea->llx;
+
+ sprintf(posname, "%dx%d", x, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+
+ while (x < corearea->urx) {
+ while (x < nextx) {
+ nx = x + (int)(roundf(gate->width * scale));
+
+ /* If next position is larger than nextx but is also */
+ /* farther from the stripe centerline than the current */
+ /* position, then break here instead. */
+ if ((nx > nextx) && ((nextx - x) < (nx - nextx))) break;
+
+ gate->placedX += (double)totalfx / (double)scale;
+
+ sprintf(posname, "%dx%d", nx, y);
+ gate = (GATE)HashLookup(posname, &CellPosTable);
+ x = nx;
+
+ if ((x >= corearea->urx) || (gate == NULL)) break;
+ }
+ if ((x >= corearea->urx) || (gate == NULL)) break;
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Add fill under stripe from (%d %d) to (%d %d)\n",
+ x, y, x + stripewidth_f, y);
+
+ for (testfill = fillseries; testfill; testfill = testfill->next) {
+ /* Create new fill instance */
+ newfillinst = (GATE)malloc(sizeof(struct gate_));
+ newfillinst->gatetype = testfill->gate;
+ sprintf(posname, "SFILL%dx%d", x + totalfx, y);
+ newfillinst->gatename = strdup(posname);
+ newfillinst->placedX = (double)(x + totalfx) / (double)scale;
+ newfillinst->placedY = (double)y / (double)scale;
+ newfillinst->clientdata = (void *)NULL;
+ row = DefFindRow(y);
+ newfillinst->orient = (row) ? row->orient : orient;
+ DefAddGateInstance(newfillinst);
+
+ /* Position will not be revisited, so no need to */
+ /* add to the position hash. */
+
+ totalfx += testfill->width;
+ }
+ nextx += stripepitch_f - stripewidth_f;
+ }
+ orient = (orient == RN) ? RS : RN;
+ }
+
+ /* Adjust pins */
+
+ for (gate = Nlgates; gate; gate = gate->next) {
+ if (gate->gatetype == NULL) {
+ int px, po, pitches;
+
+ px = (int)(roundf(gate->placedX * scale));
+ po = px - stripeoffset_f - (stripewidth_f / 2);
+ if (po > 0)
+ pitches = 1 + po / (stripepitch_f - stripewidth_f);
+ else
+ pitches = -1;
+ if (pitches <= 0) continue;
+
+ px += pitches * stripewidth_f;
+ gate->placedX = (float)(px) / scale;
+ }
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Layout stretched by %g um\n",
+ (double)totalfx / (double)scale);
+ }
+ else
+ {
+ /* Stripes are overlaid on core without stretching */
+ numstripes = corew / stripepitch_f;
+ if (numstripes < minstripes) {
+ numstripes = minstripes;
+
+ /* Recompute stripe pitch */
+ stripepitch_f = corew / numstripes;
+ tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew);
+ stripepitch_f = (tp * corearea->sitew);
+
+ fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n",
+ stripepitch_t, (float)stripepitch_f / (float)scale);
+ }
+ totalw = corew;
+
+ /* Find offset to center of 1st power stripe that results in */
+ /* centering the stripes on the layout. */
+
+ stripeoffset_i = (totalw - (numstripes - 1) * stripewidth_f) / 2;
+ tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew);
+ stripeoffset_f = (tp * corearea->sitew);
+ }
+
+ /* Record expanded area */
+ corearea->urx_exp = totalw + corearea->llx;
+
+ /* Record and return final calculated power stripe pitch and width */
+ stripevals->pitch = stripepitch_f;
+ stripevals->width = stripewidth_f;
+ stripevals->offset = stripeoffset_f;
+ stripevals->stretch = totalfx;
+ stripevals->number = numstripes;
+ return stripevals;
+}
+
+
+/*--------------------------------------------------------------*/
+/* Adjust obstructions */
+/* If OBSTRUCT flag is set, then obstructions are to be read */
+/* from the file <rootname>.obs, modified, and written to file */
+/* <rootname>.obsx. */
+/*--------------------------------------------------------------*/
+
+void
+fix_obstructions(char *definname, SINFO stripevals, float scale,
+ unsigned char Flags)
+{
+ FILE *fobsin, *fobsout;
+ char *filename, *pptr;
+ char line[256];
+ char layer[32];
+ float fllx, flly, furx, fury;
+ int illx, iurx, pitches, po;
+
+ /* If no layout stretching was done, then nothing needs to be modified */
+ if (Flags & NOSTRETCH) return;
+
+ /* Only handle obstruction layer file if the -O switch was specified */
+ /* (to do: Handle obstructions via the DEF file BLOCKAGES records) */
+
+ if (!(Flags & OBSTRUCT)) return;
+
+ filename = (char *)malloc(strlen(definname) + 6);
+ strcpy(filename, definname);
+ pptr = strrchr(filename, '.');
+ if (pptr == NULL)
+ strcat(filename, ".obs");
+ else
+ sprintf(pptr, ".obs");
+
+ fobsin = fopen(filename, "r");
+ if (fobsin == NULL) {
+ fprintf(stderr, "Cannot open obstruction file %s for reading\n", filename);
+ free(filename);
+ return;
+ }
+
+ pptr = strrchr(filename, '.');
+ sprintf(pptr, ".obsx");
+
+ fobsout = fopen(filename, "w");
+ if (fobsout == NULL) {
+ fprintf(stderr, "Cannot open obstruction file %s for writing\n", filename);
+ fclose(fobsin);
+ free(filename);
+ return;
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Modifying obstruction positions.\n");
+
+ while (1) {
+ if (fgets(line, 256, fobsin) == NULL) break;
+
+ if (!strncmp(line, "obstruction", 11)) {
+ sscanf(line + 11, "%g %g %g %g %s", &fllx, &flly, &furx, &fury, layer);
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "In: %g %g %g %g\n", fllx, flly, furx, fury);
+
+ illx = (int)(roundf(fllx * scale));
+ iurx = (int)(roundf(furx * scale));
+
+ po = illx - stripevals->offset - (stripevals->width / 2);
+ if (po > 0) {
+ pitches = 1 + po / (stripevals->pitch - stripevals->width);
+ illx += pitches * stripevals->width;
+ }
+ po = iurx - stripevals->offset - (stripevals->width / 2);
+ if (po > 0) {
+ pitches = 1 + po / (stripevals->pitch - stripevals->width);
+ iurx += pitches * stripevals->width;
+ }
+
+ fllx = (float)illx / scale;
+ furx = (float)iurx / scale;
+
+ fprintf(fobsout, "obstruction %g %g %g %g %s\n",
+ fllx, flly, furx, fury, layer);
+
+ if (Flags & VERBOSE)
+ fprintf(stdout, "Out: %g %g %g %g\n", fllx, flly, furx, fury);
+ }
+ }
+
+ free(filename);
+ fclose(fobsin);
+ fclose(fobsout);
+}
+
+/*--------------------------------------------------------------*/
+/* Create a new VIA record from a VIA or VIARULE record, with */
+/* total width and height as given. */
+/*--------------------------------------------------------------*/
+
+void
+via_make_generated(LefList viagen, LefList lefl, int lbot, int lcut,
+ int width, int height, float scale)
+{
+ float cutsizex, cutsizey;
+ float bboundx, bboundy;
+ float tboundx, tboundy;
+
+ int xcuts, ycuts;
+ char vianame[128];
+ DSEG newseg;
+ LefList cutrec;
+
+ float borderx, bordery, spacingx, spacingy;
+ float fwidth, fheight;
+ float x, y;
+ int i, j;
+
+ int ltop = lbot + 1;
+
+ /* Convert width and height to microns */
+ fwidth = (float)width / scale;
+ fheight = (float)height / scale;
+
+ sprintf(vianame, "%s_post", lefl->lefName);
+ viagen->lefName = strdup(vianame);
+
+ /* Determine number of cuts in X and Y */
+
+ cutsizex = LefGetViaWidth(lefl, lcut, 0);
+ cutsizey = LefGetViaWidth(lefl, lcut, 1);
+ bboundx = LefGetViaWidth(lefl, lbot, 0);
+ bboundy = LefGetViaWidth(lefl, lbot, 1);
+ tboundx = LefGetViaWidth(lefl, ltop, 0);
+ tboundy = LefGetViaWidth(lefl, ltop, 1);
+
+ /* Calculate number of cuts to fit */
+
+ borderx = (((tboundx > bboundx) ? tboundx : bboundx) - cutsizex) / 2;
+ bordery = (((tboundy > bboundy) ? tboundy : bboundy) - cutsizey) / 2;
+
+ /* If there is a SPACING record in the via, use it. If not, see if */
+ /* there is a SPACING record in the record for the via cut. If */
+ /* not, then assume spacing is twice the border width. */
+
+ cutrec = lefl;
+ if (cutrec->info.via.spacing == NULL) cutrec = LefFindLayerByNum(lcut);
+ if (cutrec && cutrec->info.via.spacing) {
+ spacingx = cutrec->info.via.spacing->spacing;
+ if (cutrec->info.via.spacing->next)
+ spacingy = cutrec->info.via.spacing->next->spacing;
+ else
+ spacingy = spacingx;
+ }
+ else {
+ spacingx = 2 * borderx;
+ spacingy = 2 * bordery;
+ }
+
+ xcuts = 1 + (int)((fwidth - 2 * borderx) - cutsizex) / (cutsizex + spacingx);
+ ycuts = 1 + (int)((fheight - 2 * bordery) - cutsizey) / (cutsizey + spacingy);
+
+ /* Ensure at least one cut! */
+ if (xcuts < 1) xcuts = 1;
+ if (ycuts < 1) ycuts = 1;
+
+ /* Make sure that width and height are enough to pass DRC. Height */
+ /* in particular is taken from the width of the power bus and may */
+ /* not be wide enough for the topmost contacts. */
+
+ fwidth = (xcuts * cutsizex) + ((float)(xcuts - 1) * spacingx) + (2 * borderx);
+ fheight = (ycuts * cutsizey) + ((float)(ycuts - 1) * spacingy) + (2 * bordery);
+
+ viagen->info.via.area.layer = lbot;
+ viagen->info.via.area.x1 = -fwidth / 2;
+ viagen->info.via.area.x2 = fwidth / 2;
+ viagen->info.via.area.y1 = -fheight / 2;
+ viagen->info.via.area.y2 = fheight / 2;
+
+ newseg = (DSEG)malloc(sizeof(struct dseg_));
+ newseg->layer = ltop;
+ newseg->x1 = -fwidth / 2;
+ newseg->x2 = fwidth / 2;
+ newseg->y1 = -fheight / 2;
+ newseg->y2 = fheight / 2;
+ newseg->next = NULL;
+ viagen->info.via.lr = newseg;
+
+ x = (-fwidth / 2) + borderx + (cutsizex / 2);
+ for (i = 0; i < xcuts; i++) {
+ y = (-fheight / 2) + bordery + (cutsizey / 2);
+ for (j = 0; j < ycuts; j++) {
+ newseg = (DSEG)malloc(sizeof(struct dseg_));
+ newseg->layer = lcut;
+ newseg->x1 = x - (cutsizex / 2);
+ newseg->x2 = x + (cutsizex / 2);
+ newseg->y1 = y - (cutsizey / 2);
+ newseg->y2 = y + (cutsizey / 2);
+ newseg->next = viagen->info.via.lr;
+ viagen->info.via.lr = newseg;
+ y += (cutsizey + spacingy);
+ }
+ x += (cutsizex + spacingy);
+ }
+}
+
+/*--------------------------------------------------------------*/
+/* Routine to check if a LefList entry is a valid via record */
+/* for a via between metal layers "l" (ell) and "l + 1". */
+/* Since the LefList record drops metal and cut layers more or */
+/* less randomly into the area, lr, and lr->next records, all */
+/* combinations of layers need to be checked. */
+/* */
+/* Since the metal layers are known and the cut layer is not */
+/* necessarily known, return the cut layer number if the via */
+/* record is valid. If not, return -1. */
+/*--------------------------------------------------------------*/
+
+int
+check_valid_via(LefList lefl, int l)
+{
+ int cutlayer = -1;
+
+ if (lefl->info.via.area.layer == l) {
+ if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) {
+ if (lefl->info.via.lr->next)
+ cutlayer = lefl->info.via.lr->next->layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l + 1) {
+ cutlayer = lefl->info.via.lr->layer;
+ }
+ }
+ else if (lefl->info.via.area.layer == l + 1) {
+ if (lefl->info.via.lr && lefl->info.via.lr->layer == l) {
+ if (lefl->info.via.lr->next)
+ cutlayer = lefl->info.via.lr->next->layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l) {
+ cutlayer = lefl->info.via.lr->layer;
+ }
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->layer == l) {
+ if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l + 1)
+ cutlayer = lefl->info.via.area.layer;
+ }
+ else if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) {
+ if (lefl->info.via.lr && lefl->info.via.lr->next &&
+ lefl->info.via.lr->next->layer == l)
+ cutlayer = lefl->info.via.area.layer;
+ }
+ return cutlayer;
+}
+
+/*--------------------------------------------------------------*/
+/* Generate stripe contact posts and metal */
+/*--------------------------------------------------------------*/
+
+PSTRIPE
+generate_stripes(SINFO stripevals, FILLLIST fillcells,
+ COREBBOX corearea, char *pattern,
+ char *VddNet, char *GndNet, float scale, unsigned char Flags)
+{
+ int i, j, l, p, n, y, hh;
+ int testuse;
+ int ltop, lbot;
+ float syt, syb;
+ double vw, vh;
+ int cutlayer, lcut;
+ int gnd_ymin, gnd_ymax, vdd_ymin, vdd_ymax, gate_ymin;
+ int gnd_xmin, vdd_xmin, tmp;
+ char *powername, *groundname;
+ int corew;
+
+ PSTRIPE rails = NULL, prail;
+ PPOST post;
+ LefList lefl, *vialist, topvia, vvalid;
+ DSEG lr;
+
+ /* Pick the first fill cell and parse it for the POWER and */
+ /* GROUND pins as marked by pin USE. If no pins are marked */
+ /* as use POWER or GROUND then look for pins with names */
+ /* matching the power and ground nets. */
+
+ GATE fillgate = fillcells->gate;
+ DSEG r;
+ ROW row;
+
+ lbot = 0;
+ for (i = 0; i < fillgate->nodes; i++) {
+ testuse = fillgate->use[i];
+ if (testuse == PORT_USE_POWER) {
+ powername = fillgate->node[i];
+ break;
+ }
+ }
+ if (i == fillgate->nodes) {
+ for (i = 0; i < fillgate->nodes; i++) {
+ if (!strcmp(fillgate->node[i], VddNet)) {
+ powername = VddNet;
+ lbot = fillgate->taps[i]->layer;
+ break;
+ }
+ }
+ if (i == fillgate->nodes) {
+ fprintf(stderr, "Failed to find power net pin in cell macro.\n");
+ return NULL;
+ }
+ }
+ /* NOTE: Need to parse all taps; find one that crosses the whole */
+ /* cell. If none, then find one that touches or overlaps the cell */
+ /* bottom or top. */
+
+ r = fillcells->gate->taps[i];
+ vdd_ymin = (int)(roundf(r->y1 * scale));
+ vdd_ymax = (int)(roundf(r->y2 * scale));
+ vdd_xmin = (int)(roundf(r->x1 * scale));
+
+ for (j = 0; j < fillgate->nodes; j++) {
+ testuse = fillgate->use[j];
+ if (testuse == PORT_USE_GROUND) {
+ groundname = fillgate->node[j];
+ break;
+ }
+ }
+ if (j == fillgate->nodes) {
+ for (j = 0; j < fillgate->nodes; j++) {
+ if (!strcmp(fillgate->node[j], GndNet)) {
+ groundname = GndNet;
+ lbot = fillgate->taps[i]->layer;
+ break;
+ }
+ }
+ if (j == fillgate->nodes) {
+ fprintf(stderr, "Failed to find ground net pin in cell macro.\n");
+ return NULL;
+ }
+ }
+ r = fillcells->gate->taps[j];
+ gnd_ymin = (int)(roundf(r->y1 * scale));
+ gnd_ymax = (int)(roundf(r->y2 * scale));
+ gnd_xmin = (int)(roundf(r->x1 * scale));
+
+ /* If the first row is inverted then the ymin/ymax values need to be */
+ /* adjusted. */
+
+ row = DefLowestRow(); /* Try this first */
+ if (row) {
+ if (row->orient & RS) {
+ gnd_ymax = corearea->siteh - gnd_ymax;
+ gnd_ymin = corearea->siteh - gnd_ymin;
+ vdd_ymax = corearea->siteh - vdd_ymax;
+ vdd_ymin = corearea->siteh - vdd_ymin;
+
+ tmp = gnd_ymax;
+ gnd_ymax = gnd_ymin;
+ gnd_ymin = tmp;
+
+ tmp = vdd_ymax;
+ vdd_ymax = vdd_ymin;
+ vdd_ymin = tmp;
+ }
+ }
+ else {
+ if (corearea->orient & RS) {
+ gnd_ymax = corearea->siteh - gnd_ymax;
+ gnd_ymin = corearea->siteh - gnd_ymin;
+ vdd_ymax = corearea->siteh - vdd_ymax;
+ vdd_ymin = corearea->siteh - vdd_ymin;
+
+ tmp = gnd_ymax;
+ gnd_ymax = gnd_ymin;
+ gnd_ymin = tmp;
+
+ tmp = vdd_ymax;
+ vdd_ymax = vdd_ymin;
+ vdd_ymin = tmp;
+ }
+ }
+
+ n = strlen(pattern);
+
+ /* Find the highest metal layer that is oriented vertically */
+
+ ltop = LefGetMaxRouteLayer() - 1;
+ if (LefGetRouteOrientation(ltop) == 1) ltop--;
+ if (ltop < 3) {
+ int mspace;
+
+ fprintf(stderr, "Will not generate over-cell power stripes due to lack "
+ "of route layers\n");
+ fprintf(stderr, "Generating comb structures instead.\n");
+
+ /* Generate comb structures in metal1 on either side to connect */
+ /* power and ground. */
+
+ /* Get wide spacing rule relative to stripe width */
+ mspace = (int)(roundf(LefGetRouteWideSpacing(lbot,
+ (float)(stripevals->width) / scale) * scale));
+
+ /* Account for ground or power bus extending beyond the cell */
+ /* bounding box. Assumes that the extension is symmetric on */
+ /* left and right, and the same for power and ground. */
+
+ if (gnd_xmin < 0) mspace -= gnd_xmin;
+ else if (vdd_xmin < 0) mspace -= vdd_xmin;
+ corew = corearea->sitew;
+
+ /* Generate power comb on left */
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = -mspace - stripevals->width / 2;
+ prail->pitch = corearea->urx_exp;
+ prail->num = 1;
+ if ((n < 1) || (pattern[0] == 'P')) {
+ prail->name = VddNet;
+ y = corearea->lly + (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+ else {
+ prail->name = GndNet;
+ y = corearea->lly + (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = lbot;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = (float)(corearea->lly - hh) / scale;
+ prail->stripe->y2 = (float)(corearea->ury + hh) / scale;
+ prail->posts = NULL;
+
+ /* Create all posts */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = NULL;
+ newpost->strut->layer = lbot;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = 0.0;
+ newpost->strut->x2 = (float)(-prail->offset + corew) / scale;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ prail->offset += corearea->llx;
+
+ /* Generate ground comb on right */
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = mspace + stripevals->width / 2;
+ prail->pitch = corearea->urx_exp;
+ prail->num = 1;
+ if ((n < 2) || (pattern[1] == 'G')) {
+ prail->name = GndNet;
+ y = corearea->lly + (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+ else {
+ prail->name = VddNet;
+ y = corearea->lly + (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = lbot;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = (float)(corearea->lly - hh) / scale;
+ prail->stripe->y2 = (float)(corearea->ury + hh) / scale;
+ prail->posts = NULL;
+
+ /* Create all posts */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = NULL;
+ newpost->strut->layer = lbot;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = (float)(-prail->offset - corew) / scale;
+ newpost->strut->x2 = 0.0;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ prail->offset += corearea->urx_exp;
+ return rails;
+ }
+
+ /* Generate vias for posts */
+ /* NOTE: This assumes that the power and ground rails are the same */
+ /* height. If not, need to have two vialist records, one for each rail */
+
+ vialist = (LefList *)malloc((ltop - lbot) * sizeof(LefList));
+ for (l = lbot; l < ltop; l++) vialist[l] = (LefList)NULL;
+
+ /* First find any VIARULE GENERATE vias; these are preferred, as they */
+ /* have all the right information for generating a new via for the */
+ /* post. */
+
+ for (l = lbot; l < ltop; l++) {
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->lefClass == CLASS_VIA) {
+ if (lefl->info.via.generated) {
+
+ /* Check for top and bottom layers matching (l) and (l + 1) */
+
+ if ((cutlayer = check_valid_via(lefl, l)) != -1) {
+ vialist[l] = LefNewVia(NULL);
+ via_make_generated(vialist[l], lefl, l, cutlayer,
+ stripevals->width, gnd_ymax - gnd_ymin, scale);
+ break; // Continue to next layer
+ }
+ }
+ }
+ }
+ }
+
+ /* Next find any VIAs that have the right layers. Use this information */
+ /* to create a new VIA record with the correct size for the post. */
+
+ for (l = lbot; l < ltop; l++) {
+ if (vialist[l] == NULL) {
+ vvalid = NULL;
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->lefClass == CLASS_VIA) {
+ if ((lcut = check_valid_via(lefl, l)) != -1) {
+ /* Don't include vias that this routine has created, */
+ if (strstr(lefl->lefName, "_post") != NULL) continue;
+ if (vvalid == NULL) {
+ vvalid = lefl;
+ vw = LefGetViaWidth(lefl, lcut, 0);
+ vh = LefGetViaWidth(lefl, lcut, 1);
+ cutlayer = lcut;
+ }
+ /* Find the smallest valid via. Note that it */
+ /* is preferred to find a via designed for a */
+ /* power post, if there is one (to be done). */
+ else {
+ double tw, th;
+ tw = LefGetViaWidth(lefl, lcut, 0);
+ th = LefGetViaWidth(lefl, lcut, 1);
+ if ((th < vh) || ((th == vh) && (tw < vw))) {
+ vvalid = lefl;
+ vw = tw;
+ vh = th;
+ cutlayer = lcut;
+ }
+ }
+ }
+ }
+ }
+ if (vvalid) {
+ vialist[l] = LefNewVia(NULL);
+ via_make_generated(vialist[l], vvalid, l, cutlayer,
+ stripevals->width, gnd_ymax - gnd_ymin, scale);
+ }
+ }
+ }
+
+ for (l = lbot; l < ltop; l++) {
+ if (vialist[l] == NULL) {
+ LefList ll0, ll1;
+ ll0 = LefFindLayerByNum(l);
+ ll1 = LefFindLayerByNum(l + 1);
+ fprintf(stderr, "Error: Failed to find a valid via record between "
+ "metal layers %s and %s\n",
+ ll0->lefName, ll1->lefName);
+ return NULL;
+ }
+ }
+
+ /* Construct power stripe records */
+
+ for (p = 0; p < n; p++) {
+
+ prail = (PSTRIPE)malloc(sizeof(struct _powerstripe));
+ prail->next = rails;
+ rails = prail;
+
+ prail->offset = stripevals->offset + p * stripevals->pitch;
+ prail->pitch = stripevals->pitch * n;
+ prail->num = 1 + (corearea->urx_exp - prail->offset) / prail->pitch;
+
+ /* Note this is not strdup(), so can compare string pointers */
+ prail->name = (pattern[p] == 'P') ? VddNet : GndNet;
+
+ /* Find vertical dimensions of power rails on the standard cells */
+ y = corearea->lly;
+ if (pattern[p] == 'P') {
+ y += (vdd_ymax + vdd_ymin) / 2;
+ hh = (vdd_ymax - vdd_ymin) / 2;
+ }
+ else {
+ y += (gnd_ymax + gnd_ymin) / 2;
+ hh = (gnd_ymax - gnd_ymin) / 2;
+ }
+
+ /* Query the extent of the highest via layer and make sure the */
+ /* power stripe covers it completely. */
+
+ topvia = vialist[ltop - 1];
+ syb = topvia->info.via.area.y1;
+ syt = topvia->info.via.area.y2;
+ for (lr = topvia->info.via.lr; lr; lr = lr->next) {
+ if (lr->y1 < syb) syb = lr->y1;
+ if (lr->y2 > syt) syt = lr->y2;
+ }
+
+ /* First two rails, extend down by one track pitch. A pin will */
+ /* be added here. */
+ if (p < 2) {
+ syb = -LefGetRoutePitch(ltop - 1);
+ if (syb > topvia->info.via.area.y1)
+ syb -= LefGetRoutePitch(ltop - 1);
+ }
+
+ prail->stripe = (DSEG)malloc(sizeof(struct dseg_));
+ prail->stripe->layer = ltop;
+ prail->stripe->next = NULL;
+ prail->stripe->x1 = -(float)(stripevals->width / 2) / scale;
+ prail->stripe->x2 = (float)(stripevals->width / 2) / scale;
+ prail->stripe->y1 = syb + ((float)corearea->lly / scale);
+ prail->stripe->y2 = syt + ((float)corearea->ury / scale);
+ prail->posts = NULL;
+
+ /* Create all posts (also horizontally centered at X = 0) */
+
+ /* To be done: Check if rows are alternating N and S */
+ /* or not. This code assumes that they always are. */
+
+ for (; y <= corearea->ury; y += 2 * corearea->siteh) {
+ for (l = lbot; l < ltop; l++) {
+ PPOST newpost = (PPOST)malloc(sizeof(powerpost));
+ newpost->strut = (DSEG)malloc(sizeof(struct dseg_));
+ newpost->viagen = vialist[l];
+ newpost->strut->layer = l;
+ newpost->strut->next = NULL;
+ newpost->strut->x1 = -(float)(stripevals->width / 2) / scale;
+ newpost->strut->x2 = (float)(stripevals->width / 2) / scale;
+ newpost->strut->y1 = (float)(y - hh) / scale;
+ newpost->strut->y2 = (float)(y + hh) / scale;
+ newpost->next = prail->posts;
+ prail->posts = newpost;
+ }
+ }
+ }
+
+ /* link via records and prepend to LefInfo */
+ for (l = lbot; l < ltop - 1; l++)
+ vialist[l]->next = vialist[l + 1];
+
+ vialist[l]->next = LefInfo;
+ LefInfo = vialist[0];
+
+ free(vialist);
+ return rails;
+}
+
+/*--------------------------------------------------------------*/
+/* Convert orient bitfield from GATE structure to character */
+/* string for a DEF file. */
+/*--------------------------------------------------------------*/
+
+char *gate_to_orient(int orient)
+{
+ int oidx;
+ static char *orients[8] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"};
+
+ switch (orient & (RN | RS | RE | RW)) {
+ case RS:
+ oidx = 1;
+ break;
+ case RE:
+ oidx = 2;
+ break;
+ case RW:
+ oidx = 3;
+ break;
+ default:
+ oidx = 0;
+ break;
+ }
+ if (orient & RF) oidx += 4;
+ return orients[oidx];
+}
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+void
+output_rail(FILE *outfptr, PSTRIPE rail, int x, int first, float scale)
+{
+ PPOST post;
+ LefList lefl;
+ char *otyp;
+ float fyd, fya, fxd, fxa;
+ int iyd, iya, ixd, ixa;
+
+ static char *otypes[] = {"+ FIXED", " NEW"};
+
+ otyp = (first) ? otypes[0] : otypes[1];
+
+ for (post = rail->posts; post; post = post->next) {
+ lefl = LefFindLayerByNum(post->strut->layer);
+ fyd = post->strut->y2 - post->strut->y1;
+ fya = (post->strut->y2 + post->strut->y1) / 2;
+ iyd = (int)(roundf(fyd * scale));
+ iya = (int)(roundf(fya * scale));
+ if (post->viagen) {
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * * ) %s",
+ otyp, lefl->lefName, iyd, x, iya, post->viagen->lefName);
+ }
+ else {
+ fxd = post->strut->x1;
+ ixd = x + (int)(roundf(fxd * scale));
+ fxa = post->strut->x2;
+ ixa = x + (int)(roundf(fxa * scale));
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( %d * )",
+ otyp, lefl->lefName, iyd, ixd, iya, ixa);
+ }
+ otyp = otypes[1];
+ }
+ lefl = LefFindLayerByNum(rail->stripe->layer);
+ fxd = rail->stripe->x2 - rail->stripe->x1;
+ fya = rail->stripe->y1;
+ fyd = rail->stripe->y2;
+ ixd = (int)(roundf(fxd * scale));
+ iya = (int)(roundf(fya * scale));
+ iyd = (int)(roundf(fyd * scale));
+ fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * %d )",
+ otyp, lefl->lefName, ixd, x, iya, iyd);
+}
+
+/*--------------------------------------------------------------*/
+/*--------------------------------------------------------------*/
+
+void
+output_rails(FILE *outfptr, PSTRIPE rail, COREBBOX corearea, float scale, int first)
+{
+ int i, x;
+
+ x = rail->offset;
+ for (i = 0; i < rail->num; i++) {
+ output_rail(outfptr, rail, x, first, scale);
+ first = FALSE;
+ x += rail->pitch;
+ if (x > corearea->urx_exp) break;
+ }
+}
+
+/*--------------------------------------------------------------*/
+/* write_output --- */
+/* */
+/* write the modified DEF file to the output. */
+/*--------------------------------------------------------------*/
+
+void
+write_output(char *definname, char *defoutname, float scale,
+ COREBBOX corearea, SINFO stripevals, PSTRIPE rails,
+ char *VddNet, char *GndNet, unsigned char Flags)
+{
+ FILE *outfptr, *infptr;
+ static char line[LEF_LINE_MAX + 2];
+ char *sptr;
+ int i, copyspecial = 0, numVias, foundrail[2], ltop;
+ double lh, ly;
+
+ GATE gate, endgate;
+ NET net;
+ NODE node;
+ PPOST post;
+ LefList lefl, lname, lrec;
+ DSEG seg;
+ PSTRIPE rail;
+
+ if (defoutname == NULL)
+ outfptr = stdout;
+ else {
+ outfptr = fopen(defoutname, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Failed to open file %s for writing modified output\n",
+ defoutname);
+ return;
+ }
+ }
+
+ if (Flags & VERBOSE) fprintf(stdout, "Writing DEF file output.\n");
+
+ /* Find the number of (new) power rail SPECIALNETS to be written */
+ /* There will normally be one record for power and one for ground */
+ /* unless rails were not written. Power and ground rails are */
+ /* checked separately for consistency of the output DEF. */
+
+ foundrail[0] = foundrail[1] = FALSE;
+ for (rail = rails; rail; rail = rail->next) {
+ if ((foundrail[0] == FALSE) && (rail->name == VddNet)) foundrail[0] = TRUE;
+ if ((foundrail[1] == FALSE) && (rail->name == GndNet)) foundrail[1] = TRUE;
+ }
+ numSpecial = ((foundrail[0] == TRUE) ? 1 : 0) + ((foundrail[1] == TRUE) ? 1 : 0);
+
+ /* Write DEF header (copy input DEF file verbatim up to COMPONENTS) */
+
+ infptr = fopen(definname, "r");
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before COMPONENTS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "COMPONENTS", 10)) break;
+
+ /* Rewrite DIEAREA, ROWS, and TRACKS */
+ else if (!strncmp(sptr, "DIEAREA", 7)) {
+ char *dptr;
+ int dllx, dlly, durx, dury;
+
+ dptr = strchr(line, '(');
+ sscanf(dptr + 1, "%d %d", &dllx, &dlly);
+ dptr = strchr(dptr + 1, '(');
+ sscanf(dptr + 1, "%d %d", &durx, &dury);
+
+ durx += stripevals->stretch;
+
+ fprintf(outfptr, "DIEAREA ( %d %d ) ( %d %d ) ;\n",
+ dllx, dlly, durx, dury);
+ }
+
+ else if (!strncmp(sptr, "ROW", 3)) {
+ char *dptr;
+ int radd, xnum, ridx, rowy;
+ ROW row;
+ char namepos[16];
+ radd = stripevals->stretch / corearea->sitew;
+ static char *orientations[] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"};
+
+ dptr = sptr;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rowy);
+ row = DefFindRow(rowy);
+
+ xnum = row->xnum + radd;
+ switch (row->orient & (RN | RS | RE | RW)) {
+ case RS:
+ ridx = 1;
+ break;
+ case RE:
+ ridx = 2;
+ break;
+ case RW:
+ ridx = 3;
+ break;
+ default:
+ ridx = 0;
+ }
+ if (row->orient & RF) ridx += 4;
+
+ fprintf(outfptr, "ROW %s %s %d %d %s DO %d BY %d STEP %d %d ;\n",
+ row->rowname, row->sitename, row->x, row->y,
+ orientations[ridx], xnum, row->ynum,
+ row->xstep, row->ystep);
+ }
+ else if (!strncmp(sptr, "TRACKS", 6)) {
+ char *dptr;
+ char o;
+ char layer[64];
+ int roffset, rnum, rpitch;
+
+ dptr = sptr + 6;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%c", &o);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &roffset);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rnum);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%d", &rpitch);
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ while (!isspace(*dptr)) dptr++;
+ while (isspace(*dptr)) dptr++;
+ sscanf(dptr, "%s", layer);
+
+ if (o == 'X') {
+ rnum += (int)(stripevals->stretch / rpitch);
+ if (stripevals->stretch % rpitch != 0) rnum++;
+ }
+ fprintf(outfptr, "TRACKS %c %d DO %d STEP %d LAYER %s ;\n",
+ o, roffset, rnum, rpitch, layer);
+ }
+ else
+ fprintf(outfptr, "%s", line);
+ }
+
+ /* Write generated vias for posts */
+
+ numVias = 0;
+ for (lefl = LefInfo; lefl; lefl = lefl->next)
+ if (strstr(lefl->lefName, "_post") != NULL)
+ numVias++;
+
+ fprintf(outfptr, "VIAS %d ;\n", numVias);
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ int llx, lly, urx, ury;
+
+ if (strstr(lefl->lefName, "_post") != NULL) {
+ fprintf(outfptr, "- %s\n", lefl->lefName);
+ lname = LefFindLayerByNum(lefl->info.via.area.layer);
+ llx = (int)(roundf(lefl->info.via.area.x1 * scale));
+ lly = (int)(roundf(lefl->info.via.area.y1 * scale));
+ urx = (int)(roundf(lefl->info.via.area.x2 * scale));
+ ury = (int)(roundf(lefl->info.via.area.y2 * scale));
+ fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )",
+ lname->lefName, llx, lly, urx, ury);
+ if (lefl->info.via.lr) fprintf(outfptr, "\n");
+ for (seg = lefl->info.via.lr; seg; seg = seg->next) {
+ lname = LefFindLayerByNum(seg->layer);
+ llx = (int)(roundf(seg->x1 * scale));
+ lly = (int)(roundf(seg->y1 * scale));
+ urx = (int)(roundf(seg->x2 * scale));
+ ury = (int)(roundf(seg->y2 * scale));
+ fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )",
+ lname->lefName, llx, lly, urx, ury);
+ if (seg->next) fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, " ;\n");
+ }
+ }
+ fprintf(outfptr, "END VIAS\n\n");
+
+ for (endgate = Nlgates; endgate->next; endgate = endgate->next);
+
+ if (Numgates > 0) {
+
+ /* Write instances (COMPONENTS) in the order read */
+ fprintf(outfptr, "COMPONENTS %d ;\n", Numgates);
+ for (gate = endgate; gate ; gate = gate->last) {
+ int px, py;
+ if (gate->gatetype == NULL) continue;
+
+ px = (int)(roundf(gate->placedX * scale));
+ py = (int)(roundf(gate->placedY * scale));
+ fprintf(outfptr, "- %s %s + PLACED ( %d %d ) %s ;\n",
+ gate->gatename, gate->gatetype->gatename,
+ px, py, gate_to_orient(gate->orient));
+ }
+ fprintf(outfptr, "END COMPONENTS\n\n");
+ }
+
+ if (Numpins > 0) {
+ int llx, lly, urx, ury, px, py;
+ static char *pin_classes[] = {
+ "DEFAULT", "INPUT", "OUTPUT", "OUTPUT TRISTATE", "INOUT", "FEEDTHRU"
+ };
+ LefList lefl;
+
+ /* Write instances (PINS) in the order read, plus power pins */
+
+ fprintf(outfptr, "PINS %d ;\n", Numpins + numSpecial);
+
+ if (foundrail[0]) {
+ for (rail = rails; rail; rail = rail->next)
+ if (rail->name == VddNet) break;
+
+ ltop = rail->stripe->layer;
+ lrec = LefFindLayerByNum(ltop);
+ lh = LefGetRoutePitch(ltop - 1) / 4;
+ ly = rail->stripe->y1 + lh;
+
+ /* NOTE: The over-simplified "arrangepins" Tcl script expects */
+ /* LAYER and PLACED records to be on separate lines. This will */
+ /* eventually be replaced by a C coded executable with a more */
+ /* rigorous parser, like addspacers uses. */
+
+ fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", VddNet, VddNet);
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lrec->lefName,
+ (int)(roundf(rail->stripe->x1 * scale)),
+ (int)(roundf(-lh * scale)),
+ (int)(roundf(rail->stripe->x2 * scale)),
+ (int)(roundf(lh * scale)));
+ fprintf(outfptr, " + PLACED ( %d %d ) N ;\n",
+ rail->offset, (int)(roundf(ly * scale)));
+ }
+ if (foundrail[1]) {
+ for (rail = rails; rail; rail = rail->next)
+ if (rail->name == GndNet) break;
+
+ ltop = rail->stripe->layer;
+ lrec = LefFindLayerByNum(ltop);
+ lh = LefGetRoutePitch(ltop - 1) / 4;
+ ly = rail->stripe->y1 + lh;
+
+ fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", GndNet, GndNet);
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lrec->lefName,
+ (int)(roundf(rail->stripe->x1 * scale)),
+ (int)(roundf(-lh * scale)),
+ (int)(roundf(rail->stripe->x2 * scale)),
+ (int)(roundf(lh * scale)));
+ fprintf(outfptr, " + PLACED ( %d %d ) N ;\n",
+ rail->offset, (int)(roundf(ly * scale)));
+ }
+
+ for (gate = endgate; gate ; gate = gate->last) {
+ int dir;
+
+ if (gate->gatetype != NULL) continue;
+
+ fprintf(outfptr, "- %s + NET %s",
+ gate->gatename, gate->node[0]);
+
+ if (gate->direction[0] != 0)
+ fprintf(outfptr, " + DIRECTION %s",
+ pin_classes[gate->direction[0]]);
+
+ fprintf(outfptr, "\n");
+
+ lefl = LefFindLayerByNum(gate->taps[0]->layer);
+ urx = (int)(roundf((gate->taps[0]->x2 - gate->taps[0]->x1) * scale) / 2.0);
+ ury = (int)(roundf((gate->taps[0]->y2 - gate->taps[0]->y1) * scale) / 2.0);
+ llx = -urx;
+ lly = -ury;
+ px = (int)(roundf(gate->placedX * scale));
+ py = (int)(roundf(gate->placedY * scale));
+
+ fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n",
+ lefl->lefName, llx, lly, urx, ury);
+ fprintf(outfptr, " + PLACED ( %d %d ) %s ;\n",
+ px, py, gate_to_orient(gate->orient));
+ }
+ fprintf(outfptr, "END PINS\n\n");
+ }
+
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before NETS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "NETS", 4)) break;
+
+ }
+ fprintf(outfptr, "%s", line);
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) {
+ fprintf(stderr, "Error: End of file reached before END NETS.\n");
+ return;
+ }
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ /* Assuming a typical DEF file here. . . */
+ if (!strncmp(sptr, "SPECIALNETS", 11)) {
+ sscanf(sptr + 11, "%d", &copyspecial);
+ break;
+ }
+ else if (!strncmp(sptr, "END DESIGN", 10)) {
+ break;
+ }
+ fprintf(outfptr, "%s", line);
+ }
+
+ /* Rewrite SPECIALNETS line with updated number */
+
+ if (copyspecial + numSpecial > 0)
+ fprintf(outfptr, "SPECIALNETS %d ;\n", numSpecial + copyspecial);
+
+ if (numSpecial > 0) {
+ /* Write power bus stripes (SPECIALNETS) */
+ int i, first = TRUE;
+ char *railnames[2] = {GndNet, VddNet};
+
+ for (i = 0; i < 2; i++) {
+ fprintf(outfptr, "- %s", railnames[i]);
+ for (rail = rails; rail; rail = rail->next) {
+ if (rail->name == railnames[i]) {
+ output_rails(outfptr, rail, corearea, scale, first);
+ first = FALSE;
+ }
+ }
+ fprintf(outfptr, " ;\n");
+ first = TRUE;
+ }
+ }
+
+ /* If there were previously no SPECIALNETS then add the ending line */
+ if (numSpecial > 0 && copyspecial == 0)
+ fprintf(outfptr, "END SPECIALNETS\n\n");
+
+ /* Copy the remainder of the file verbatim */
+
+ while (1) {
+ if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) break;
+ sptr = line;
+ while (isspace(*sptr)) sptr++;
+
+ if (!strncmp(sptr, "END DESIGN", 10)) {
+ break;
+ }
+ fprintf(outfptr, "%s", line);
+ }
+ fprintf(outfptr, "END DESIGN\n");
+ fclose(infptr);
+
+ if (defoutname != NULL) fclose(outfptr);
+ fflush(stdout);
+}
+
+/*--------------------------------------------------------------*/
+/* helpmessage - tell user how to use the program */
+/* */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "addspacers [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "addspacers adds fill cells and power buses to a layout.\n");
+ fprintf(outf, "Output on stdout unless redirected with -o option.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, " -o <path> Output file path and name\n");
+ fprintf(outf, " -l <path> Path to standard cell LEF file (for macro list)\n");
+ fprintf(outf, " -p <name> Name of power net\n");
+ fprintf(outf, " -g <name> Name of ground net\n");
+ fprintf(outf, " -f <name> Name of fill cell (or prefix)\n");
+ fprintf(outf, " -w <width> Power bus stripe width\n");
+ fprintf(outf, " -P <pitch> Power bus stripe pitch\n");
+ fprintf(outf, " -s <pattern> Power bus stripe pattern (default \"PG\") \n");
+ fprintf(outf, " -n Do not stretch layout under power buses.\n");
+ fprintf(outf, " -O Handle obstruction areas in separate .obs file\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -v Verbose output\n");
+ fprintf(outf, " -h Print this message\n");
+
+} /* helpmessage() */
+
diff --git a/src/blif2BSpice.c b/src/blif2BSpice.c
index e1418f7..95414d6 100644
--- a/src/blif2BSpice.c
+++ b/src/blif2BSpice.c
@@ -279,7 +279,7 @@ void ReadNetlistAndConvert(FILE *netfile, FILE *libfile, char *libname,
CleanupString(MainSubcktName);
fprintf(outfile, "*SPICE netlist created from BLIF module "
"%s by blif2BSpice\n", MainSubcktName);
- fprintf(outfile, "");
+ fprintf(outfile, "\n");
/* If doinclude == 1 then dump the contents of the */
/* libraray. If 0, then just write a .include line. */
@@ -291,7 +291,7 @@ void ReadNetlistAndConvert(FILE *netfile, FILE *libfile, char *libname,
while (loc_getline(line, sizeof(line), libfile) > 0)
fputs(line, outfile);
fclose(libfile);
- fprintf(outfile, "");
+ fprintf(outfile, "\n");
}
}
else {
diff --git a/src/blif2Verilog.c b/src/blif2Verilog.c
index 0464a06..78df6cb 100644
--- a/src/blif2Verilog.c
+++ b/src/blif2Verilog.c
@@ -31,14 +31,12 @@ extern char *optarg;
#define INPUT 0
#define OUTPUT 1
-#define UNKNOWN 2
struct Vect {
struct Vect *next;
char *name;
char direction; /* INPUT or OUTPUT */
int Max;
- int Min;
};
void ReadNetlistAndConvert(FILE *, FILE *, unsigned char);
@@ -191,6 +189,7 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
First = TRUE;
Vector = VectorAlloc();
+ Vector->next = NULL;
while (loc_getline(line, sizeof(line), NETFILE) > 0) {
lptr = line;
while (isspace(*lptr)) lptr++;
@@ -220,8 +219,6 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
if (strcmp(VectorPresent->name, InputName) == 0) {
VectorPresent->Max = (VectorPresent->Max > VectorIndex) ?
VectorPresent->Max : VectorIndex;
- VectorPresent->Min = (VectorPresent->Min < VectorIndex) ?
- VectorPresent->Min : VectorIndex;
Found = TRUE;
}
VectorPresent = VectorPresent->next;
@@ -229,8 +226,9 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
if (!Found) {
VectorPresent->name = strdup(InputName);
VectorPresent->direction = INPUT;
- VectorPresent->Max = VectorPresent->Min = VectorIndex;
+ VectorPresent->Max = VectorIndex;
VectorPresent->next = VectorAlloc();
+ VectorPresent->next->next = NULL;
}
}
if (PrintIt || !Found) { // Should print vectors in module statement
@@ -283,8 +281,6 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
if (strcmp(VectorPresent->name, OutputName) == 0) {
VectorPresent->Max = (VectorPresent->Max > VectorIndex) ?
VectorPresent->Max : VectorIndex;
- VectorPresent->Min = (VectorPresent->Min < VectorIndex) ?
- VectorPresent->Min : VectorIndex;
Found = TRUE;
}
VectorPresent = VectorPresent->next;
@@ -292,8 +288,9 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
if (!Found) {
VectorPresent->name = strdup(OutputName);
VectorPresent->direction = OUTPUT;
- VectorPresent->Max = VectorPresent->Min = VectorIndex;
+ VectorPresent->Max = VectorIndex;
VectorPresent->next = VectorAlloc();
+ VectorPresent->next->next = NULL;
}
}
if (PrintIt || !Found) {
@@ -335,11 +332,10 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
VectorPresent = Vector;
while (VectorPresent->next != NULL) {
- fprintf(OUT, "%s [%d:%d] %s;\n",
+ fprintf(OUT, "%s [%d:0] %s;\n",
(VectorPresent->direction == INPUT) ?
"input" : "output",
VectorPresent->Max,
- VectorPresent->Min,
VectorPresent->name);
VectorPresent = VectorPresent->next;
}
@@ -412,12 +408,15 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags)
}
}
if (!ItIsAnInput && !ItIsAnOutput) {
- while ((Weirdpnt = strchr(InstancePortWire,'[')) != NULL)
+ if ((Weirdpnt = strchr(InstancePortWire,'[')) != NULL) {
*Weirdpnt = '_';
- while ((Weirdpnt = strchr(InstancePortWire,']')) != NULL)
- *Weirdpnt = '_';
- while ((Weirdpnt = strchr(InstancePortWire, '$')) != NULL)
+ if ((Weirdpnt = strchr(InstancePortWire,']')) != NULL)
+ *Weirdpnt = '_';
+ }
+ while ((Weirdpnt = strchr(InstancePortWire, '$')) != NULL) {
*Weirdpnt = '_';
+ Weirdpnt++;
+ }
}
@@ -617,16 +616,7 @@ int loc_getline( char s[], int lim, FILE *fp)
struct Vect *VectorAlloc(void)
{
- struct Vect *newvector;
-
- newvector = (struct Vect *) malloc(sizeof(struct Vect));
- newvector->next = NULL;
- newvector->name = NULL;
- newvector->direction = UNKNOWN;
- newvector->Max = 0;
- newvector->Min = 0;
-
- return newvector;
+ return (struct Vect *) malloc(sizeof(struct Vect));
}
diff --git a/src/blifFanout.c b/src/blifFanout.c
index 6215bb6..910d484 100644
--- a/src/blifFanout.c
+++ b/src/blifFanout.c
@@ -125,7 +125,7 @@ struct Gatelist {
double strength;
} Gatelist_;
-struct hashlist *Gatehash[OBJHASHSIZE];
+struct hashtable Gatehash;
struct Nodelist {
char ignore;
@@ -142,7 +142,7 @@ struct Nodelist {
int curcount; // Active count for fanout buffering trees
} Nodelist_;
-struct hashlist *Nodehash[OBJHASHSIZE];
+struct hashtable Nodehash;
struct Drivelist {
char *Separator; // Separator (e.g., "X")
@@ -151,7 +151,7 @@ struct Drivelist {
int NgatesOut; // Number of gates with this suffix in output
} Drivelist_;
-struct hashlist *Drivehash[OBJHASHSIZE];
+struct hashtable Drivehash;
struct Baselist {
char *BaseName; // gate base name (e.g., "INV")
@@ -159,7 +159,7 @@ struct Baselist {
struct Gatelist **gates; // list of pointers to gates with
} Baselist_;
-struct hashlist *Basehash[OBJHASHSIZE];
+struct hashtable Basehash;
enum states_ {NONE, INPUTS, OUTPUTS, GATENAME, PINNAME, INPUTNODE, CLOCKNODE,
OUTPUTNODE, ENDMODEL, ERROR};
@@ -288,10 +288,10 @@ int main (int argc, char *argv[])
hashfunc = hash;
matchfunc = match;
- InitializeHashTable(Nodehash);
- InitializeHashTable(Drivehash);
- InitializeHashTable(Gatehash);
- InitializeHashTable(Basehash);
+ InitializeHashTable(&Nodehash, LARGEHASHSIZE);
+ InitializeHashTable(&Drivehash, LARGEHASHSIZE);
+ InitializeHashTable(&Gatehash, LARGEHASHSIZE);
+ InitializeHashTable(&Basehash, LARGEHASHSIZE);
fprintf(stdout, "blifFanout for qflow " QFLOW_VERSION "." QFLOW_REVISION "\n");
@@ -440,9 +440,9 @@ int main (int argc, char *argv[])
// Determine if suffix is numeric or alphabetic
if (gatecount > 0) {
char *suffix;
- gl = (struct Gatelist *)HashFirst(Gatehash);
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
while (gl && gl->suffix == NULL)
- gl = (struct Gatelist *)HashNext(Gatehash);
+ gl = (struct Gatelist *)HashNext(&Gatehash);
if (gl && gl->suffix && !isdigit(*gl->suffix))
SuffixIsNumeric = FALSE;
}
@@ -462,7 +462,7 @@ int main (int argc, char *argv[])
gl = (struct Gatelist *)NULL;
if (Buffername != NULL) {
- gl = (struct Gatelist *)HashLookup(Buffername, Gatehash);
+ gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
if (gl == NULL) {
fprintf(stderr, "No buffer \"%s\" found in gate list\n", Buffername);
fprintf(stderr, "Searching gate list for suitable buffer.\n");
@@ -471,7 +471,7 @@ int main (int argc, char *argv[])
if ((gl == NULL) || (Buffername == NULL)) {
// Find a suitable buffer
- gl = (struct Gatelist *)HashFirst(Gatehash);
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
while (gl != NULL) {
Cell *ctest;
@@ -501,11 +501,11 @@ int main (int argc, char *argv[])
}
}
}
- gl = (struct Gatelist *)HashNext(Gatehash);
+ gl = (struct Gatelist *)HashNext(&Gatehash);
}
}
else
- gl = (struct Gatelist *)HashLookup(Buffername, Gatehash);
+ gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
if (gl == NULL) {
if (Buffername == NULL)
@@ -553,7 +553,7 @@ int main (int argc, char *argv[])
while (t) {
switch (state) {
case GATENAME:
- gl = (struct Gatelist *)HashLookup(t, Gatehash);
+ gl = (struct Gatelist *)HashLookup(t, &Gatehash);
if (gl != NULL) {
if (VerboseFlag) printf("\n\n%s", t);
gateinputs = gl->num_inputs;
@@ -650,7 +650,7 @@ int main (int argc, char *argv[])
/* Show top fanout gate */
nlmax = NULL;
nlimax = NULL;
- nl = (struct Nodelist *)HashFirst(Nodehash);
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
while (nl != NULL) {
if (nl->outputgatestrength != 0.0) {
nl->ratio = nl->total_load / nl->outputgatestrength;
@@ -674,7 +674,7 @@ int main (int argc, char *argv[])
Inputload = nl->total_load;
}
}
- nl = (struct Nodelist *)HashNext(Nodehash);
+ nl = (struct Nodelist *)HashNext(&Nodehash);
}
if (VerboseFlag) printf("\n");
@@ -699,7 +699,7 @@ int main (int argc, char *argv[])
if (doFanout && ((Topfanout > MaxFanout) || (Inputfanout > MaxFanout))) {
/* Insert buffer trees */
- nl = (struct Nodelist *)HashFirst(Nodehash);
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
while (nl != NULL) {
if (nl->ignore == FALSE) {
@@ -735,7 +735,7 @@ int main (int argc, char *argv[])
Buffer_count += n;
}
}
- nl = (struct Nodelist *)HashNext(Nodehash);
+ nl = (struct Nodelist *)HashNext(&Nodehash);
}
}
write_output(doLoadBalance, infptr, outfptr);
@@ -745,14 +745,14 @@ int main (int argc, char *argv[])
fprintf(stderr, "%d gates were changed.\n", Changed_count);
fprintf(stderr, "\nGate counts by drive strength:\n\n");
- dl = (struct Drivelist *)HashFirst(Drivehash);
+ dl = (struct Drivelist *)HashFirst(&Drivehash);
while (dl != NULL) {
if (dl->NgatesIn > 0) {
fprintf(stderr, "\t\"%s%s\" gates\tIn: %d \tOut: %d \t%+d\n",
dl->Separator, dl->DriveType, dl->NgatesIn,
dl->NgatesOut, (dl->NgatesOut - dl->NgatesIn));
}
- dl = (struct Drivelist *)HashNext(Drivehash);
+ dl = (struct Drivelist *)HashNext(&Drivehash);
}
fprintf(stderr, "\n");
@@ -795,7 +795,7 @@ void read_ignore_file(char *ignore_file_name)
while (*sp != '\0' && *sp != '\n' && !isspace(*sp)) sp++;
*sp = '\0';
- nl = (struct Nodelist *)HashLookup(s, Nodehash);
+ nl = (struct Nodelist *)HashLookup(s, &Nodehash);
if (nl != NULL) {
nl->ignore = (char)1;
}
@@ -853,7 +853,7 @@ int read_gate_file(char *gate_file_name, char *separator)
// gl->gatename, gl->gatecell->name, gl->gatecell->function);
gl->strength = MaxLatency / gl->delay;
- HashPtrInstall(gl->gatename, gl, Gatehash);
+ HashPtrInstall(gl->gatename, gl, &Gatehash);
gatecount++;
/* Install prefix in Basehash. Note that prefix contains the */
@@ -866,10 +866,10 @@ int read_gate_file(char *gate_file_name, char *separator)
ssave = gl->gatename[ind];
gl->gatename[ind] = '\0';
- bl = (struct Baselist *)HashLookup(gl->gatename, Basehash);
+ bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash);
if (bl == NULL) {
bl = BaselistAlloc();
- HashPtrInstall(gl->gatename, bl, Basehash);
+ HashPtrInstall(gl->gatename, bl, &Basehash);
bl->BaseName = strdup(gl->gatename);
}
gl->gatename[ind] = ssave;
@@ -975,7 +975,7 @@ void showgatelist(void)
Pin *curpin;
double pincap;
- gl = (struct Gatelist *)HashFirst(Gatehash);
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
while (gl != NULL) {
printf("\n\ngate: %s with %d inputs and %g drive strength\n",
@@ -989,7 +989,7 @@ void showgatelist(void)
printf("%g ", pincap);
}
}
- gl = (struct Gatelist *)HashNext(Gatehash);
+ gl = (struct Gatelist *)HashNext(&Gatehash);
}
}
@@ -1003,13 +1003,13 @@ void registernode(char *nodename, int type, struct Gatelist *gl, char *pinname)
struct Nodelist *nl;
double pincap;
- nl = (struct Nodelist *)HashLookup(nodename, Nodehash);
+ nl = (struct Nodelist *)HashLookup(nodename, &Nodehash);
if (nl == NULL) {
nl = NodelistAlloc();
nl->nodename = strdup(nodename);
if (type == OUTPUT) nl->outputgate = NULL;
- HashPtrInstall(nodename, nl, Nodehash);
+ HashPtrInstall(nodename, nl, &Nodehash);
nl->type = type;
}
@@ -1050,13 +1050,13 @@ void count_gatetype(struct Gatelist *gl, int num_in, int num_out)
if ((s = gl->suffix) == NULL)
return;
- dl = (struct Drivelist *)HashLookup(s, Drivehash);
+ dl = (struct Drivelist *)HashLookup(s, &Drivehash);
if (dl == NULL) {
// New drive type found
dl = DrivelistAlloc();
- HashPtrInstall(s, dl, Drivehash);
+ HashPtrInstall(s, dl, &Drivehash);
dl->DriveType = strdup(s);
dl->Separator = gl->separator;
}
@@ -1075,13 +1075,13 @@ void shownodes(void)
struct Nodelist *nl;
int i;
- nl = (struct Nodelist *)HashFirst(Nodehash);
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
while (nl != NULL) {
printf("\n\nnode: %s with %d fanout and %g fF cap",
nl->nodename, nl->num_inputs, nl->total_load);
printf("\ndriven by %s, with %g strength.\n",
nl->outputgate->gatename, nl->outputgatestrength);
- nl = (struct Nodelist *)HashNext(Nodehash);
+ nl = (struct Nodelist *)HashNext(&Nodehash);
}
}
@@ -1124,7 +1124,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
hasended = 0;
// Find the gate record corresponding to the buffer name
- glbuf = (struct Gatelist *)HashLookup(Buffername, Gatehash);
+ glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
rewind(infptr);
@@ -1155,7 +1155,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
/* Insert any added buffers (before 1st gate) */
- nl = (struct Nodelist *)HashFirst(Nodehash);
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
while (nl != NULL) {
for (i = nl->num_buf - 1; i >= 0; i--) {
hier = 0;
@@ -1189,7 +1189,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
/* the buffer tree is even deeper, incrementing */
/* M until the name is unique. */
- nltest = (struct Nodelist *)HashLookup(nodename, Nodehash);
+ nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash);
if (nltest != NULL) {
sprintf(nodename, "%s_hier%d", nl->nodename, hier);
hier++;
@@ -1206,11 +1206,11 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
nl->nodename, buf_out_pin,
nodename);
}
- nl = (struct Nodelist *)HashNext(Nodehash);
+ nl = (struct Nodelist *)HashNext(&Nodehash);
}
firstseen = 1;
}
- gl = (struct Gatelist *)HashLookup(t, Gatehash);
+ gl = (struct Gatelist *)HashLookup(t, &Gatehash);
if (gl == NULL) {
fprintf(stderr, "Error: Gate \"%s\" is used in source "
"but has no liberty file definition.\n", t);
@@ -1249,7 +1249,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
case INPUTNODE: case CLOCKNODE:
if (VerboseFlag) printf("\nInput node %s", t);
- nl = (struct Nodelist *)HashLookup(t, Nodehash);
+ nl = (struct Nodelist *)HashLookup(t, &Nodehash);
if (nl->num_buf > 0) {
hier = 0;
nltest = nl;
@@ -1282,7 +1282,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
/* the buffer tree is even deeper, incrementing M */
/* until the name is unique. */
- nltest = (struct Nodelist *)HashLookup(nodename, Nodehash);
+ nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash);
if (nltest != NULL) {
sprintf(nodename, "%s_hier%d", nl->nodename, hier);
hier++;
@@ -1307,7 +1307,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
case OUTPUTNODE:
if (VerboseFlag) printf("\nOutput node %s", t);
- nl = (struct Nodelist *)HashLookup(t, Nodehash);
+ nl = (struct Nodelist *)HashLookup(t, &Nodehash);
if (doLoadBalance && (nl != NULL)) {
if ((nl->ignore == FALSE) && (nl->ratio > 1.0)) {
if (VerboseFlag)
@@ -1394,7 +1394,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr)
fprintf(stderr, "May need to add information to gate.cfg file\n");
}
- dl = (struct Drivelist *)HashLookup(bbest->suffix, Drivehash);
+ dl = (struct Drivelist *)HashLookup(bbest->suffix, &Drivehash);
if (dl != NULL) dl->NgatesOut++;
/* Recompute size of the gate driving the buffer */
@@ -1499,7 +1499,7 @@ struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload)
ssave = gl->gatename[ind];
gl->gatename[ind] = '\0';
- bl = (struct Baselist *)HashLookup(gl->gatename, Basehash);
+ bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash);
gl->gatename[ind] = ssave;
for (i = 0; bl && (i < bl->Ndrives); i++) {
diff --git a/src/hash.c b/src/hash.c
index c18f9a2..70ffd07 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -26,8 +26,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#endif
#include "hash.h"
-unsigned long (*hashfunc)(char *) = NULL;
-int (*matchfunc)(char *, char *) = NULL;
+unsigned long (*hashfunc)(char *, int) = hash;
+int (*matchfunc)(char *, char *) = match;
int (*matchintfunc)(char *, char *, int, int) = NULL;
/* Add match functions: These are just strcmp() and */
@@ -45,13 +45,17 @@ int matchnocase(char *st1, char *st2)
else return 0;
}
-void InitializeHashTable(struct hashlist **tab)
+void InitializeHashTable(struct hashtable *table, int hashsize)
{
int i;
- for (i = 0; i < OBJHASHSIZE; i++) tab[i] = NULL;
+ table->hashsize = hashsize;
+ table->hashfirstindex = 0;
+ table->hashfirstptr = NULL;
+ table->hashtab = (struct hashlist **)malloc(hashsize * sizeof(struct hashlist *));
+ for (i = 0; i < hashsize; i++) table->hashtab[i] = NULL;
}
-int RecurseHashTable(struct hashlist **hashtab,
+int RecurseHashTable(struct hashtable *table,
int (*func)(struct hashlist *elem))
/* returns the sum of the return values of (*func) */
{
@@ -59,8 +63,8 @@ int RecurseHashTable(struct hashlist **hashtab,
struct hashlist *p;
sum = 0;
- for (i = 0; i < OBJHASHSIZE; i++)
- for (p = hashtab[i]; p != NULL; p = p->next)
+ for (i = 0; i < table->hashsize; i++)
+ for (p = table->hashtab[i]; p != NULL; p = p->next)
sum += (*func)(p);
return(sum);
}
@@ -69,15 +73,15 @@ int RecurseHashTable(struct hashlist **hashtab,
* type int value to the function.
*/
-int RecurseHashTableValue(struct hashlist **hashtab,
+int RecurseHashTableValue(struct hashtable *table,
int (*func)(struct hashlist *elem, int), int value)
{
int i, sum;
struct hashlist *p;
sum = 0;
- for (i = 0; i < OBJHASHSIZE; i++)
- for (p = hashtab[i]; p != NULL; p = p->next)
+ for (i = 0; i < table->hashsize; i++)
+ for (p = table->hashtab[i]; p != NULL; p = p->next)
sum += (*func)(p, value);
return(sum);
}
@@ -88,7 +92,7 @@ int RecurseHashTableValue(struct hashlist **hashtab,
* function through that structure.
*/
-struct nlist *RecurseHashTablePointer(struct hashlist **hashtab,
+struct nlist *RecurseHashTablePointer(struct hashtable *table,
struct nlist *(*func)(struct hashlist *elem, void *),
void *pointer)
{
@@ -96,8 +100,8 @@ struct nlist *RecurseHashTablePointer(struct hashlist **hashtab,
struct hashlist *p;
struct nlist *tp;
- for (i = 0; i < OBJHASHSIZE; i++) {
- for (p = hashtab[i]; p != NULL; p = p->next) {
+ for (i = 0; i < table->hashsize; i++) {
+ for (p = table->hashtab[i]; p != NULL; p = p->next) {
tp = (*func)(p, pointer);
if (tp != NULL) return tp;
}
@@ -142,23 +146,23 @@ static unsigned char uppercase[] = {
// have exactly the same hash result. Lousy for binning and even
// lousier for generating class magic numbers.
-unsigned long hashnocase(char *s)
+unsigned long hashnocase(char *s, int hashsize)
{
unsigned long hashval;
for (hashval = 0; *s != '\0'; )
hashval = uppercase[*s++]
+ (hashval << 6) + (hashval << 16) - hashval;
- return (hashval % OBJHASHSIZE);
+ return (hashval % hashsize);
}
-unsigned long hash(char *s)
+unsigned long hash(char *s, int hashsize)
{
unsigned long hashval;
for (hashval = 0; *s != '\0'; )
hashval = (*s++) + (hashval << 6) + (hashval << 16) - hashval;
- return (hashval % OBJHASHSIZE);
+ return (hashval % hashsize);
}
/*----------------------------------------------------------------------*/
@@ -166,14 +170,14 @@ unsigned long hash(char *s)
/* return the 'ptr' field of the hash table entry, or NULL if not found */
/*----------------------------------------------------------------------*/
-void *HashLookup(char *s, struct hashlist **hashtab)
+void *HashLookup(char *s, struct hashtable *table)
{
struct hashlist *np;
unsigned long hashval;
- hashval = (*hashfunc)(s);
+ hashval = (*hashfunc)(s, table->hashsize);
- for (np = hashtab[hashval]; np != NULL; np = np->next)
+ for (np = table->hashtab[hashval]; np != NULL; np = np->next)
if ((*matchfunc)(s, np->name))
return (np->ptr); /* correct match */
return (NULL); /* not found */
@@ -186,14 +190,14 @@ void *HashLookup(char *s, struct hashlist **hashtab)
/* passed integer value i */
/*----------------------------------------------------------------------*/
-void *HashIntLookup(char *s, int i, struct hashlist **hashtab)
+void *HashIntLookup(char *s, int i, struct hashtable *table)
{
struct hashlist *np;
unsigned long hashval;
- hashval = (*hashfunc)(s);
+ hashval = (*hashfunc)(s, table->hashsize);
- for (np = hashtab[hashval]; np != NULL; np = np->next) {
+ for (np = table->hashtab[hashval]; np != NULL; np = np->next) {
if (np->ptr == NULL) {
if ((*matchintfunc)(s, np->name, i, -1))
return NULL;
@@ -210,14 +214,13 @@ void *HashIntLookup(char *s, int i, struct hashlist **hashtab)
/* return the hashlist entry, after (re)initializing its 'ptr' field */
/*----------------------------------------------------------------------*/
-struct hashlist *HashPtrInstall(char *name, void *ptr,
- struct hashlist **hashtab)
+struct hashlist *HashPtrInstall(char *name, void *ptr, struct hashtable *table)
{
struct hashlist *np;
unsigned long hashval;
- hashval = (*hashfunc)(name);
- for (np = hashtab[hashval]; np != NULL; np = np->next)
+ hashval = (*hashfunc)(name, table->hashsize);
+ for (np = table->hashtab[hashval]; np != NULL; np = np->next)
if ((*matchfunc)(name, np->name)) {
np->ptr = ptr;
return (np); /* match found in hash table */
@@ -229,8 +232,8 @@ struct hashlist *HashPtrInstall(char *name, void *ptr,
if ((np->name = strdup(name)) == NULL)
return (NULL);
np->ptr = ptr;
- np->next = hashtab[hashval];
- return(hashtab[hashval] = np);
+ np->next = table->hashtab[hashval];
+ return(table->hashtab[hashval] = np);
}
/*----------------------------------------------------------------------*/
@@ -239,13 +242,13 @@ struct hashlist *HashPtrInstall(char *name, void *ptr,
/*----------------------------------------------------------------------*/
struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr,
- struct hashlist **hashtab)
+ struct hashtable *table)
{
struct hashlist *np;
unsigned long hashval;
- hashval = (*hashfunc)(name);
- for (np = hashtab[hashval]; np != NULL; np = np->next)
+ hashval = (*hashfunc)(name, table->hashsize);
+ for (np = table->hashtab[hashval]; np != NULL; np = np->next)
if ((*matchintfunc)(name, np->name, value, (int)(*((int *)np->ptr)))) {
np->ptr = ptr;
return (np); /* match found in hash table */
@@ -256,27 +259,29 @@ struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr,
return (NULL);
if ((np->name = strdup(name)) == NULL) return (NULL);
np->ptr = ptr;
- np->next = hashtab[hashval];
- return(hashtab[hashval] = np);
+ np->next = table->hashtab[hashval];
+ return(table->hashtab[hashval] = np);
}
/*----------------------------------------------------------------------*/
/* destroy a hash table, freeing associated memory */
/*----------------------------------------------------------------------*/
-void HashKill(struct hashlist **hashtab)
+void HashKill(struct hashtable *table)
{
struct hashlist *np, *p;
int i;
- for (i = 0; i < OBJHASHSIZE; i++) {
- for (p = hashtab[i]; p != NULL; ) {
+ for (i = 0; i < table->hashsize; i++) {
+ for (p = table->hashtab[i]; p != NULL; ) {
np = p->next;
free(p->name);
free(p);
p = np;
}
}
+ free(table->hashtab);
+ table->hashtab = NULL;
}
/*----------------------------------------------------------------------*/
@@ -284,13 +289,13 @@ void HashKill(struct hashlist **hashtab)
/* to the new hash entry. */
/*----------------------------------------------------------------------*/
-struct hashlist *HashInstall(char *name, struct hashlist **hashtab)
+struct hashlist *HashInstall(char *name, struct hashtable *table)
{
struct hashlist *np;
unsigned long hashval;
- hashval = (*hashfunc)(name);
- for (np = hashtab[hashval]; np != NULL; np = np->next)
+ hashval = (*hashfunc)(name, table->hashsize);
+ for (np = table->hashtab[hashval]; np != NULL; np = np->next)
if ((*matchfunc)(name, np->name)) return (np); /* match found in hash table */
/* not in table, so install it */
@@ -298,27 +303,27 @@ struct hashlist *HashInstall(char *name, struct hashlist **hashtab)
return (NULL);
if ((np->name = strdup(name)) == NULL) return (NULL);
np->ptr = NULL;
- np->next = hashtab[hashval];
- return(hashtab[hashval] = np);
+ np->next = table->hashtab[hashval];
+ return(table->hashtab[hashval] = np);
}
/*----------------------------------------------------------------------*/
/* frees a hash table entry, (but not the 'ptr' field) */
/*----------------------------------------------------------------------*/
-void HashDelete(char *name, struct hashlist **hashtab)
+void HashDelete(char *name, struct hashtable *table)
{
unsigned long hashval;
struct hashlist *np;
struct hashlist *np2;
- hashval = (*hashfunc)(name);
- np = hashtab[hashval];
+ hashval = (*hashfunc)(name, table->hashsize);
+ np = table->hashtab[hashval];
if (np == NULL) return;
if ((*matchfunc)(name, np->name)) {
/* it is the first element in the list */
- hashtab[hashval] = np->next;
+ table->hashtab[hashval] = np->next;
free(np->name);
free(np);
return;
@@ -341,19 +346,19 @@ void HashDelete(char *name, struct hashlist **hashtab)
/* HashDelete with additional integer value matching */
/*----------------------------------------------------------------------*/
-void HashIntDelete(char *name, int value, struct hashlist **hashtab)
+void HashIntDelete(char *name, int value, struct hashtable *table)
{
unsigned long hashval;
struct hashlist *np;
struct hashlist *np2;
- hashval = (*hashfunc)(name);
- np = hashtab[hashval];
+ hashval = (*hashfunc)(name, table->hashsize);
+ np = table->hashtab[hashval];
if (np == NULL) return;
if ((*matchintfunc)(name, np->name, value, (int)(*((int *)np->ptr)))) {
/* it is the first element in the list */
- hashtab[hashval] = np->next;
+ table->hashtab[hashval] = np->next;
free(np->name);
free(np);
return;
@@ -375,31 +380,28 @@ void HashIntDelete(char *name, int value, struct hashlist **hashtab)
/*----------------------------------------------------------------------*/
-static int hashfirstindex; /* was long */
-static struct hashlist *hashfirstptr;
-
-void *HashNext(struct hashlist **hashtab)
+void *HashNext(struct hashtable *table)
/* returns 'ptr' field of next element, NULL when done */
{
- if (hashfirstptr != NULL && hashfirstptr->next != NULL) {
- hashfirstptr = hashfirstptr->next;
- return(hashfirstptr->ptr);
+ if (table->hashfirstptr != NULL && table->hashfirstptr->next != NULL) {
+ table->hashfirstptr = table->hashfirstptr->next;
+ return(table->hashfirstptr->ptr);
}
- while (hashfirstindex < OBJHASHSIZE) {
- if ((hashfirstptr = hashtab[hashfirstindex++]) != NULL) {
- return(hashfirstptr->ptr);
+ while (table->hashfirstindex < table->hashsize) {
+ if ((table->hashfirstptr = table->hashtab[table->hashfirstindex++]) != NULL) {
+ return(table->hashfirstptr->ptr);
}
}
- hashfirstindex = 0;
- hashfirstptr = NULL;
+ table->hashfirstindex = 0;
+ table->hashfirstptr = NULL;
return(NULL);
}
-void *HashFirst(struct hashlist **hashtab)
+void *HashFirst(struct hashtable *table)
{
- hashfirstindex = 0;
- hashfirstptr = NULL;
- return(HashNext(hashtab));
+ table->hashfirstindex = 0;
+ table->hashfirstptr = NULL;
+ return(HashNext(table));
}
diff --git a/src/hash.h b/src/hash.h
index a7e9d86..380832d 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -1,8 +1,9 @@
#ifndef _HASH_H
#define _HASH_H
-// #define OBJHASHSIZE 997
-#define OBJHASHSIZE 99997
+#define TINYHASHSIZE 17
+#define SMALLHASHSIZE 997
+#define LARGEHASHSIZE 99997
struct hashlist {
char *name;
@@ -10,43 +11,49 @@ struct hashlist {
struct hashlist *next;
};
-extern void InitializeHashTable(struct hashlist **tab);
-extern int RecurseHashTable(struct hashlist **hashtab,
+struct hashtable {
+ int hashsize;
+ int hashfirstindex; /* for iterating through table */
+ struct hashlist *hashfirstptr; /* ditto */
+ struct hashlist **hashtab; /* this is the actual table */
+};
+
+extern void InitializeHashTable(struct hashtable *table, int hashsize);
+extern int RecurseHashTable(struct hashtable *table,
int (*func)(struct hashlist *elem));
-extern int RecurseHashTableValue(struct hashlist **hashtab,
+extern int RecurseHashTableValue(struct hashtable *table,
int (*func)(struct hashlist *elem, int), int);
-extern struct nlist *RecurseHashTablePointer(struct hashlist **hashtab,
- struct nlist *(*func)(struct hashlist *elem,
+extern struct nlist *RecurseHashTablePointer(struct hashtable *table,
+ struct nlist *(*func)(struct hashlist *elem,
void *), void *pointer);
-
extern int CountHashTableEntries(struct hashlist *p);
extern int CountHashTableBinsUsed(struct hashlist *p);
-extern void HashDelete(char *name, struct hashlist **hashtab);
-extern void HashIntDelete(char *name, int value, struct hashlist **hashtab);
-extern void HashKill(struct hashlist **hashtab);
+extern void HashDelete(char *name, struct hashtable *table);
+extern void HashIntDelete(char *name, int value, struct hashtable *table);
+extern void HashKill(struct hashtable *table);
/* these functions return a pointer to a hash list element */
-extern struct hashlist *HashInstall(char *name, struct hashlist **hashtab);
+extern struct hashlist *HashInstall(char *name, struct hashtable *table);
extern struct hashlist *HashPtrInstall(char *name, void *ptr,
- struct hashlist **hashtab);
+ struct hashtable *table);
extern struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr,
- struct hashlist **hashtab);
+ struct hashtable *table);
-/* these functions return the ->ptr field of a struct hashlist */
-extern void *HashLookup(char *s, struct hashlist **hashtab);
-extern void *HashIntLookup(char *s, int i, struct hashlist **hashtab);
-extern void *HashFirst(struct hashlist **hashtab);
-extern void *HashNext(struct hashlist **hashtab);
+/* these functions return the ->ptr field of a struct hashtable */
+extern void *HashLookup(char *s, struct hashtable *table);
+extern void *HashIntLookup(char *s, int i, struct hashtable *table);
+extern void *HashFirst(struct hashtable *table);
+extern void *HashNext(struct hashtable *table);
-extern unsigned long hashnocase(char *s);
-extern unsigned long hash(char *s);
+extern unsigned long hashnocase(char *s, int);
+extern unsigned long hash(char *s, int);
extern int (*matchfunc)(char *, char *);
/* matchintfunc() compares based on the name and the first */
/* entry of the pointer value, which is cast as an integer */
extern int (*matchintfunc)(char *, char *, int, int);
-extern unsigned long (*hashfunc)(char *);
+extern unsigned long (*hashfunc)(char *, int);
/* the matching functions themselves */
extern int match(char *s1, char *s2);
diff --git a/src/lef.h b/src/lef.h
new file mode 100644
index 0000000..bed50c1
--- /dev/null
+++ b/src/lef.h
@@ -0,0 +1,239 @@
+/*--------------------------------------------------------------*/
+/* lef.h -- */
+/*--------------------------------------------------------------*/
+
+#ifndef _LEF_H
+#define _LEF_H
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef _SYS_TYPES_H
+#ifndef u_char
+typedef unsigned char u_char;
+#endif
+#ifndef u_short
+typedef unsigned short u_short;
+#endif
+#ifndef u_int
+typedef unsigned int u_int;
+#endif
+#ifndef u_long
+typedef unsigned long u_long;
+#endif
+#endif /* _SYS_TYPES_H */
+
+/* Compare functions aren't defined in the Mac's standard library */
+#if defined(__APPLE__)
+typedef int (*__compar_fn_t)(const void*, const void*);
+#endif
+
+/* Maximum number of route layers */
+#define MAX_LAYERS 12
+
+/* Maximum number of all defined layers. Since masterslice and */
+/* overlap types are ignored, this just includes all the cuts. */
+#define MAX_TYPES (MAX_LAYERS * 2 - 1)
+
+/* Cell name (and other names) max length */
+#define MAX_NAME_LEN 1024
+
+/* Max reasonable line length */
+#define MAX_LINE_LEN 2048
+
+// define possible gate orientations
+// Basic definitions for flipping in X and Y
+#define MNONE 0x00
+#define MX 0x01
+#define MY 0x02
+
+// Complete definition from value in DEF file
+#define RN 0x04
+#define RS 0x08
+#define RE 0x10
+#define RW 0x20
+#define RF 0x40
+
+// linked list structure for holding a list of char * strings
+
+typedef struct linkedstring_ *LinkedStringPtr;
+
+typedef struct linkedstring_ {
+ char *name;
+ LinkedStringPtr next;
+} LinkedString;
+
+// structure holding input and output scalefactors
+
+typedef struct scalerec_ {
+ int iscale;
+ int mscale;
+ double oscale;
+} ScaleRec;
+
+// Linked string list
+
+typedef struct string_ *STRING;
+
+struct string_ {
+ STRING next;
+ char *name;
+};
+
+/* DSEG is used for gate node and obstruction positions. */
+
+typedef struct dseg_ *DSEG;
+
+struct dseg_ {
+ DSEG next;
+ int layer;
+ double x1, y1, x2, y2;
+};
+
+/* POINT is an integer point in three dimensions (layer giving the */
+/* vertical dimension). */
+
+typedef struct point_ *POINT;
+
+struct point_ {
+ POINT next;
+ int layer;
+ int x1, y1;
+};
+
+/* DPOINT is a point location with coordinates given *both* as an */
+/* integer (for the grid-based routing) and as a physical dimension */
+/* (microns). */
+
+typedef struct dpoint_ *DPOINT;
+
+struct dpoint_ {
+ DPOINT next;
+ int layer;
+ double x, y;
+ int gridx, gridy;
+};
+
+/* BUS is used for keep information about pins that are array components */
+
+typedef struct bus_ *BUS;
+
+struct bus_ {
+ BUS next;
+ char *busname;
+ int low;
+ int high;
+};
+
+typedef struct node_ *NODE;
+
+struct node_ {
+ NODE next;
+ int nodenum; // node ordering within its net
+ DPOINT taps; // point position for node taps
+ DPOINT extend; // point position within halo of the tap
+ char *netname; // name of net this node belongs to
+ u_char numtaps; // number of actual reachable taps
+ int netnum; // number of net this node belongs to
+ int numnodes; // number of nodes on this net
+ int branchx; // position of the node branch in x
+ int branchy; // position of the node branch in y
+};
+
+// these are instances of gates in the netlist. The description of a
+// given gate (the macro) is held in GateInfo. The same structure is
+// used for both the macro and the instance records.
+
+typedef struct gate_ *GATE;
+
+struct gate_ {
+ GATE next;
+ GATE last; // For double-linked list
+ char *gatename; // Name of instance
+ GATE gatetype; // Pointer to macro record
+ u_char gateclass; // LEF class of gate (CORE, PAD, etc.)
+ u_char gatesubclass; // LEF sub-class of gate (SPACER, TIELOW, etc.)
+ int nodes; // number of nodes on this gate
+ char **node; // names of the pins on this gate
+ int *netnum; // net number connected to each pin
+ NODE *noderec; // node record for each pin
+ float *area; // gate area for each pin
+ u_char *direction; // port direction (input, output, etc.)
+ u_char *use; // pin use (power, ground, etc.)
+ DSEG *taps; // list of gate node locations and layers
+ DSEG obs; // list of obstructions in gate
+ BUS bus; // linked list of buses in the pin list
+ double width, height;
+ double placedX;
+ double placedY;
+ int orient;
+ void *clientdata; // This space for rent
+};
+
+// Define record holding information pointing to a gate and the
+// index into a specific node of that gate.
+
+typedef struct gatenode_ *GATENODE;
+
+struct gatenode_ {
+ GATE gate;
+ int idx;
+};
+
+// Structure for a network
+
+typedef struct net_ *NET;
+
+struct net_ {
+ int netnum; // a unique number for this net
+ char *netname;
+ NODE netnodes; // list of nodes connected to the net
+ int numnodes; // number of nodes connected to the net
+ char Flags; // See flag field definitions, below
+};
+
+// Flag definitions for nets
+
+#define NET_SPECIAL 1 // Indicates a net read from SPECIALNETS
+
+// List of nets
+
+typedef struct netlist_ *NETLIST;
+
+struct netlist_ {
+ NETLIST next;
+ NET net;
+};
+
+// Structure for a row definition
+
+typedef struct row_ *ROW;
+
+struct row_ {
+ char *rowname;
+ char *sitename;
+ int x;
+ int y;
+ int orient;
+ int xnum;
+ int ynum;
+ int xstep;
+ int ystep;
+};
+
+/* external references to global variables */
+
+extern GATE GateInfo; // standard cell macro information
+extern GATE PinMacro; // macro definition for a pin
+extern GATE Nlgates;
+extern NET *Nlnets;
+extern int Numnets;
+extern u_char Verbose;
+
+/* Function prototypes */
+
+#endif /* _LEF_H */
diff --git a/src/rc2dly.c b/src/rc2dly.c
index 52025cf..7936870 100644
--- a/src/rc2dly.c
+++ b/src/rc2dly.c
@@ -42,7 +42,9 @@
#include <getopt.h>
#include <time.h>
+#include "hash.h"
#include "readliberty.h" /* liberty file database */
+#include "readverilog.h" /* verilog netlist reader */
#define SRC 0x01 // node is a driver
#define SNK 0x02 // node is a receiver
@@ -56,6 +58,10 @@
#define VISIT_CAP 1
#define VISIT_RES 2
+/* Hash tables */
+struct hashtable LibHash;
+struct hashtable InstHash;
+
typedef struct _r *rptr;
typedef struct _node *nodeptr;
@@ -86,6 +92,8 @@ typedef struct _node {
short visited;
} node;
+/*----------------------------------------------------------*/
+
void print_node (nodeptr node) {
printf("Name: %s\n", node->name);
printf("Type: %d\n", node->type);
@@ -128,16 +136,24 @@ typedef struct _elmdly_item {
void print_help () {
printf("NAME\n");
- printf(" rc2dly - convert qrouter RC output file to Vesta delay file\n\n");
+ printf(" rc2dly - convert qrouter RC output file to delay file\n\n");
printf("SYNOPSIS\n");
- printf(" rc2dly -r <rc_file_name> -l <stdcell_liberty_file_name> -o <output_delay_file_name>\n");
+ printf(" rc2dly -r <rc_file_name> -l <stdcell_liberty_file_name> -d <output_delay_file_name>\n");
+ printf(" -V <verilog_netlist_name>\n");
printf("\n");
printf("DESCRIPTION\n");
- printf(" TBD\n");
+ printf(" Converts output of qrouter to one of three different delay formats,\n");
+ printf(" depending on the file extension of the -d argument. If the extension\n");
+ printf(" is .spef, then SPEF format is used. If the extension is .sdf, then\n");
+ printf(" SDF format is used. Otherwise, the input format used by vesta is\n");
+ printf(" generated.\n");
+ printf("\n");
printf("Required Arguments\n");
printf(" -r <rc_file_name>\n");
printf(" -l <stdcell_liberty_file_name\n");
- printf("OPTIONS\n");
+ printf(" -V <verilog_netlist_name\n");
+ printf("\n");
+ printf("Optional Arguments\n");
printf(" -d <output_delay_file_name>\n");
printf(" -c <module_pin_capacitance_in_pF>\n");
printf("\n");
@@ -474,6 +490,8 @@ int main (int argc, char* argv[]) {
FILE* libfile = NULL;
FILE* rcfile = NULL;
+ struct cellrec *topcell = NULL;
+
int verbose = 0;
double modulePinCapacitance = 0;
@@ -515,6 +533,7 @@ int main (int argc, char* argv[]) {
static struct option long_options[] = {
{"rc-file" , required_argument , 0, 'r'},
{"liberty-file" , required_argument , 0, 'l'},
+ {"verilog-file" , required_argument , 0, 'V'},
{"delay-file" , required_argument , 0, 'd'},
{"pin-capacitance" , required_argument , 0, 'c'},
{"delimiter" , required_argument , 0, 'D'},
@@ -526,7 +545,7 @@ int main (int argc, char* argv[]) {
/* getopt_long stores the option index here. */
int option_index = 0;
- c = getopt_long (argc, argv, "hv:r:l:d:D:", long_options, &option_index);
+ c = getopt_long (argc, argv, "hv:r:l:d:D:V:", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
@@ -560,6 +579,10 @@ int main (int argc, char* argv[]) {
optarg, strerror(errno));
break;
+ case 'V':
+ topcell = ReadVerilog(optarg);
+ break;
+
case 'l':
libfile = fopen(optarg, "r");
if (libfilename) free(libfilename);
@@ -631,6 +654,11 @@ int main (int argc, char* argv[]) {
return 1;
}
+ if (topcell == NULL) {
+ fprintf(stderr, "ERROR: Must specify input verilog netlist.\n");
+ return 1;
+ }
+
if (cells == NULL) {
fprintf(stderr, "ERROR: No cells were read from Liberty timing files.\n");
return 5;
@@ -658,6 +686,25 @@ int main (int argc, char* argv[]) {
char **tokens;
int num_toks = 0;
+ struct instance *inst;
+
+ InitializeHashTable(&LibHash, SMALLHASHSIZE);
+ InitializeHashTable(&InstHash, LARGEHASHSIZE);
+
+ /* Hash the liberty cell records */
+
+ for (newcell = cells; newcell; newcell = newcell->next)
+ HashPtrInstall(newcell->name, newcell, &LibHash);
+
+ /* Hash the verilog instances by liberty cell record */
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ newcell = (Cell *)HashLookup(inst->cellname, &LibHash);
+ HashPtrInstall(inst->instname, newcell, &InstHash);
+ }
+
+ /* LibHash is no longer needed */
+ HashKill(&LibHash);
if (format == FORMAT_SPEF) {
char outstr[200];
@@ -868,14 +915,14 @@ int main (int argc, char* argv[]) {
if (!strncmp(tokens[2], "PIN/", 4))
snprintf(currnode->mapped, 12, "*%d", net_idx);
else {
- pname = strrchr(tokens[2], '/');
+ pname = strrchr(tokens[2], '/') + 1;
if (pname)
snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
- delimiter, ++pname);
+ delimiter, pname);
else
- /* Node name is probably hosed, but don't crash */
+ /* Node name is hosed but don't crash the program */
snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
- delimiter, tokens[2]);
+ delimiter, tokens[2]);
}
if (verbose > 1) print_node(currnode);
@@ -979,7 +1026,7 @@ int main (int argc, char* argv[]) {
snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
delimiter, ++pname);
else
- /* Node name is probably hosed, but don't crash */
+ /* Node name is hosed but don't crash the program */
snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
delimiter, tokens[t]);
}
@@ -1027,7 +1074,7 @@ int main (int argc, char* argv[]) {
//fprintf(stdout, "Found pin as receiver: %s\n", tokens[t]);
} else {
- cell = get_cell_by_name(cells, cellName);
+ cell = (Cell *)HashLookup(cellName, &InstHash);
Pin *tmpPin = NULL;
if (cell != NULL) {
@@ -1085,7 +1132,7 @@ int main (int argc, char* argv[]) {
if (verbose > 3) print_node(last_driver->node);
elmdlyptr currElm = calloc(1, sizeof(elmdly));
- currElm->name = calloc(1, sizeof(char) * strlen(tokens[0]));
+ currElm->name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 1));
// name the Elmore Delay after the net
strcpy(currElm->name, tokens[0]);
currElm->src = last_driver->node;
diff --git a/src/readdef.c b/src/readdef.c
new file mode 100644
index 0000000..6d4c3fa
--- /dev/null
+++ b/src/readdef.c
@@ -0,0 +1,2091 @@
+/*
+ * readdef.c --
+ *
+ * This module incorporates the LEF/DEF format for standard-cell place and
+ * route.
+ *
+ * Version 0.1 (September 26, 2003): DEF input of designs.
+ *
+ * Written by Tim Edwards, Open Circuit Design
+ * Modified April 2013 for use with qrouter
+ * Modified December 2018 for use with qflow (DEF2Verilog, back-annotate verilog
+ * netlist from DEF output).
+ *
+ * It is assumed that the LEF files have been read in prior to this, and
+ * layer information is already known. The DEF file should have information
+ * primarily on die are, track placement, pins, components, and nets.
+ *
+ * Routed nets have their routes dropped into track obstructions, and the
+ * nets are ignored.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <math.h> /* for roundf() function, if std=c99 */
+
+#include "readlef.h"
+#include "readdef.h"
+#include "hash.h"
+
+int numSpecial = 0; /* Tracks number of specialnets */
+int Numnets = 0; /* Total nets, including special nets */
+int Numgates = 0; /* Number of components */
+int Numpins = 0; /* Number of pins */
+
+/* These hash tables speed up DEF file reading */
+
+struct hashtable InstanceTable;
+struct hashtable NetTable;
+struct hashtable RowTable;
+char *DEFDesignName;
+
+DSEG UserObs = NULL;
+double Xlowerbound = 0, Xupperbound = 0, Ylowerbound = 0, Yupperbound = 0;
+double PitchX = 1.0, PitchY = 1.0;
+NET *Nlnets = NULL;
+GATE Nlgates = NULL;
+GATE PinMacro;
+
+/*--------------------------------------------------------------*/
+/* Cell macro lookup based on the hash table */
+/*--------------------------------------------------------------*/
+
+static void
+DefHashInit(void)
+{
+ /* Initialize the macro hash table */
+
+ InitializeHashTable(&InstanceTable, LARGEHASHSIZE);
+ InitializeHashTable(&NetTable, LARGEHASHSIZE);
+ InitializeHashTable(&RowTable, TINYHASHSIZE);
+}
+
+GATE
+DefFindGate(char *name)
+{
+ GATE ginst;
+
+ ginst = (GATE)HashLookup(name, &InstanceTable);
+ return ginst;
+}
+
+ROW
+DefFindRow(int yval)
+{
+ ROW row;
+ char namepos[32];
+
+ sprintf(namepos, "%d", yval);
+ row = (ROW)HashLookup(namepos, &RowTable);
+ return row;
+}
+
+/*--------------------------------------------------------------*/
+/* Find the lowest row and return the row record. */
+/*--------------------------------------------------------------*/
+
+struct nlist *rowfindlowest(struct hashlist *p, void *clientdata)
+{
+ ROW row = (ROW)(p->ptr);
+ ROW *lrow = (ROW *)clientdata;
+
+ if ((*lrow == NULL) || (row->y < (*lrow)->y))
+ *lrow = row;
+
+ return NULL;
+}
+
+/*--------------------------------------------------------------*/
+
+ROW
+DefLowestRow()
+{
+ ROW row = NULL;
+
+ RecurseHashTablePointer(&RowTable, rowfindlowest, (void *)(&row));
+
+ return row;
+}
+
+/*--------------------------------------------------------------*/
+
+NET
+DefFindNet(char *name)
+{
+ NET net;
+
+ // Guard against calls to find nets before DEF file is read
+ if (Numnets == 0) return NULL;
+
+ net = (NET)HashLookup(name, &NetTable);
+ return net;
+}
+
+/*--------------------------------------------------------------*/
+/* Cell macro hash table generation */
+/* Given an instance record, create an entry in the hash table */
+/* for the instance name, with the record entry pointing to the */
+/* instance record. */
+/*--------------------------------------------------------------*/
+
+static void
+DefHashInstance(GATE gateginfo)
+{
+ HashPtrInstall(gateginfo->gatename, gateginfo, &InstanceTable);
+}
+
+/*--------------------------------------------------------------*/
+
+char *
+DefDesign()
+{
+ return DEFDesignName;
+}
+
+/*--------------------------------------------------------------*/
+/* Net hash table generation */
+/* Given a net record, create an entry in the hash table for */
+/* the net name, with the record entry pointing to the net */
+/* record. */
+/*--------------------------------------------------------------*/
+
+static void
+DefHashNet(NET net)
+{
+ HashPtrInstall(net->netname, net, &NetTable);
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefAddRoutes --
+ *
+ * Parse a network route statement from the DEF file.
+ * If "special" is 1, then, add the geometry to the
+ * list of obstructions. If "special" is 0, then read
+ * the geometry into a route structure for the net.
+ *
+ * Results:
+ * Returns the last token encountered.
+ *
+ * Side Effects:
+ * Reads from input stream;
+ * Adds information to the layout database.
+ *
+ *------------------------------------------------------------
+ */
+
+static char *
+DefAddRoutes(FILE *f, float oscale, NET net, char special)
+{
+ char *token;
+ DSEG lr, drect;
+ struct point_ refp;
+ char valid = FALSE; /* is there a valid reference point? */
+ char noobstruct;
+ char initial = TRUE;
+ struct dseg_ locarea;
+ double x, y, lx, ly, w, hw, s;
+ int routeLayer = -1, paintLayer;
+ LefList lefl;
+
+ refp.x1 = 0;
+ refp.y1 = 0;
+
+ /* Don't create obstructions or routes on routed specialnets inputs */
+ noobstruct = (special == (char)1) ? TRUE : FALSE;
+
+ while (initial || (token = LefNextToken(f, TRUE)) != NULL)
+ {
+ /* Get next point, token "NEW", or via name */
+ if (initial || !strcmp(token, "NEW") || !strcmp(token, "new"))
+ {
+ /* initial pass is like a NEW record, but has no NEW keyword */
+ initial = FALSE;
+
+ /* invalidate reference point */
+ valid = FALSE;
+
+ token = LefNextToken(f, TRUE);
+ routeLayer = LefFindLayerNum(token);
+
+ if (routeLayer < 0)
+ {
+ LefError(DEF_ERROR, "Unknown layer type \"%s\" for NEW route\n", token);
+ continue;
+ }
+ paintLayer = routeLayer;
+
+ if (special == (char)1)
+ {
+ /* SPECIALNETS has the additional width */
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%lg", &w) != 1)
+ {
+ LefError(DEF_ERROR, "Bad width in special net\n");
+ continue;
+ }
+ if (w != 0)
+ w /= oscale;
+ else
+ w = LefGetRouteWidth(paintLayer);
+ }
+ else
+ w = LefGetRouteWidth(paintLayer);
+
+ }
+ else if (*token != '(') /* via name */
+ {
+ /* A '+' or ';' record ends the route */
+ if (*token == ';' || *token == '+')
+ break;
+
+ else if (valid == FALSE)
+ {
+ LefError(DEF_ERROR, "Route has via name \"%s\" but no points!\n", token);
+ continue;
+ }
+ lefl = LefFindLayer(token);
+ if (lefl != NULL)
+ {
+ /* The area to paint is derived from the via definitions. */
+
+ if (lefl != NULL)
+ {
+ if (lefl->lefClass == CLASS_VIA) {
+
+ // Note: layers may be defined in any order, metal or cut.
+ // Check both via.area and via.lr layers, and reject those
+ // that exceed the number of metal layers (those are cuts).
+
+ paintLayer = 100;
+ routeLayer = -1;
+ routeLayer = lefl->info.via.area.layer;
+ if (routeLayer < paintLayer) paintLayer = routeLayer;
+ if ((routeLayer >= 0) && (special == (char)1) &&
+ (valid == TRUE) && (noobstruct == FALSE)) {
+ s = LefGetRouteSpacing(routeLayer);
+ drect = (DSEG)malloc(sizeof(struct dseg_));
+ drect->x1 = x + (lefl->info.via.area.x1 / 2.0) - s;
+ drect->x2 = x + (lefl->info.via.area.x2 / 2.0) + s;
+ drect->y1 = y + (lefl->info.via.area.y1 / 2.0) - s;
+ drect->y2 = y + (lefl->info.via.area.y2 / 2.0) + s;
+ drect->layer = routeLayer;
+ drect->next = UserObs;
+ UserObs = drect;
+ }
+ for (lr = lefl->info.via.lr; lr; lr = lr->next) {
+ routeLayer = lr->layer;
+ if (routeLayer < paintLayer) paintLayer = routeLayer;
+ if ((routeLayer >= 0) && (special == (char)1) &&
+ (valid == TRUE) && (noobstruct == FALSE)) {
+ s = LefGetRouteSpacing(routeLayer);
+ drect = (DSEG)malloc(sizeof(struct dseg_));
+ drect->x1 = x + (lr->x1 / 2.0) - s;
+ drect->x2 = x + (lr->x2 / 2.0) + s;
+ drect->y1 = y + (lr->y1 / 2.0) - s;
+ drect->y2 = y + (lr->y2 / 2.0) + s;
+ drect->layer = routeLayer;
+ drect->next = UserObs;
+ UserObs = drect;
+ }
+ }
+ if (routeLayer == -1) paintLayer = lefl->type;
+ }
+ else {
+ paintLayer = lefl->type;
+ if (special == (char)1)
+ s = LefGetRouteSpacing(paintLayer);
+ }
+ }
+ else
+ {
+ LefError(DEF_ERROR, "Error: Via \"%s\" named but undefined.\n", token);
+ paintLayer = routeLayer;
+ }
+ }
+ else
+ LefError(DEF_ERROR, "Via name \"%s\" unknown in route.\n", token);
+ }
+ else
+ {
+ /* Revert to the routing layer type, in case we painted a via */
+ paintLayer = routeLayer;
+
+ /* Record current reference point */
+ locarea.x1 = refp.x1;
+ locarea.y1 = refp.y1;
+ lx = x;
+ ly = y;
+
+ /* Read an (X Y) point */
+ token = LefNextToken(f, TRUE); /* read X */
+ if (*token == '*')
+ {
+ if (valid == FALSE)
+ {
+ LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n");
+ goto endCoord;
+ }
+ }
+ else if (sscanf(token, "%lg", &x) == 1)
+ {
+ x /= oscale; // In microns
+ /* Note: offsets and stubs are always less than half a pitch, */
+ /* so round to the nearest integer grid point. */
+ refp.x1 = (int)(0.5 + ((x - Xlowerbound) / PitchX));
+ }
+ else
+ {
+ LefError(DEF_ERROR, "Cannot parse X coordinate.\n");
+ goto endCoord;
+ }
+ token = LefNextToken(f, TRUE); /* read Y */
+ if (*token == '*')
+ {
+ if (valid == FALSE)
+ {
+ LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n");
+ goto endCoord;
+ }
+ }
+ else if (sscanf(token, "%lg", &y) == 1)
+ {
+ y /= oscale; // In microns
+ refp.y1 = (int)(0.5 + ((y - Ylowerbound) / PitchY));
+ }
+ else
+ {
+ LefError(DEF_ERROR, "Cannot parse Y coordinate.\n");
+ goto endCoord;
+ }
+
+ /* Indicate that we have a valid reference point */
+
+ if (valid == FALSE)
+ {
+ valid = TRUE;
+ }
+ else if ((locarea.x1 != refp.x1) && (locarea.y1 != refp.y1))
+ {
+ /* Skip over nonmanhattan segments, reset the reference */
+ /* point, and output a warning. */
+
+ LefError(DEF_ERROR, "Can't deal with nonmanhattan geometry in route.\n");
+ locarea.x1 = refp.x1;
+ locarea.y1 = refp.y1;
+ lx = x;
+ ly = y;
+ }
+ else
+ {
+ locarea.x2 = refp.x1;
+ locarea.y2 = refp.y1;
+
+ if (special != (char)0) {
+ if ((valid == TRUE) && (noobstruct == FALSE)) {
+ s = LefGetRouteSpacing(routeLayer);
+ hw = w / 2;
+ drect = (DSEG)malloc(sizeof(struct dseg_));
+ if (lx > x) {
+ drect->x1 = x - s;
+ drect->x2 = lx + s;
+ }
+ else if (lx < x) {
+ drect->x1 = lx - s;
+ drect->x2 = x + s;
+ }
+ else {
+ drect->x1 = x - hw - s;
+ drect->x2 = x + hw + s;
+ }
+ if (ly > y) {
+ drect->y1 = y - s;
+ drect->y2 = ly + s;
+ }
+ else if (ly < y) {
+ drect->y1 = ly - s;
+ drect->y2 = y + s;
+ }
+ else {
+ drect->y1 = y - hw - s;
+ drect->y2 = y + hw + s;
+ }
+ drect->layer = routeLayer;
+ drect->next = UserObs;
+ UserObs = drect;
+ }
+ }
+ }
+
+endCoord:
+ /* Find the closing parenthesis for the coordinate pair */
+ while (*token != ')')
+ token = LefNextToken(f, TRUE);
+ }
+ }
+
+ return token; /* Pass back the last token found */
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadGatePin ---
+ *
+ * Given a gate name and a pin name in a net from the
+ * DEF file NETS section, find the position of the
+ * gate, then the position of the pin within the gate,
+ * and add pin and obstruction information to the grid
+ * network.
+ *
+ *------------------------------------------------------------
+ */
+
+static void
+DefReadGatePin(NET net, NODE node, char *instname, char *pinname)
+{
+ int i;
+ GATE gateginfo;
+ DSEG drect;
+ GATE g;
+ double dx, dy;
+ int gridx, gridy;
+ DPOINT dp;
+
+ g = DefFindGate(instname);
+ if (g) {
+
+ gateginfo = g->gatetype;
+
+ if (!gateginfo) {
+ // Instances marked "<net>/pin have a NULL gatetype; this is okay.
+ if (strcmp(pinname, "pin"))
+ LefError(DEF_ERROR, "Endpoint %s/%s of net %s not found\n",
+ instname, pinname, net->netname);
+ return;
+ }
+ for (i = 0; i < gateginfo->nodes; i++) {
+ if (!strcasecmp(gateginfo->node[i], pinname)) {
+ node->taps = (DPOINT)NULL;
+ node->extend = (DPOINT)NULL;
+
+ for (drect = g->taps[i]; drect; drect = drect->next) {
+
+ // Add all routing gridpoints that fall inside
+ // the rectangle. Much to do here:
+ // (1) routable area should extend 1/2 route width
+ // to each side, as spacing to obstructions allows.
+ // (2) terminals that are wide enough to route to
+ // but not centered on gridpoints should be marked
+ // in some way, and handled appropriately.
+
+ gridx = (int)((drect->x1 - Xlowerbound) / PitchX) - 1;
+
+ if (gridx < 0) gridx = 0;
+ while (1) {
+ dx = (gridx * PitchX) + Xlowerbound;
+ if (dx > drect->x2) break;
+ if (dx < drect->x1) {
+ gridx++;
+ continue;
+ }
+ gridy = (int)((drect->y1 - Ylowerbound) / PitchY) - 1;
+
+ if (gridy < 0) gridy = 0;
+ while (1) {
+ dy = (gridy * PitchY) + Ylowerbound;
+ if (dy > drect->y2) break;
+ if (dy < drect->y1) {
+ gridy++;
+ continue;
+ }
+
+ // Routing grid point is an interior point
+ // of a gate port. Record the position
+
+ dp = (DPOINT)malloc(sizeof(struct dpoint_));
+ dp->layer = drect->layer;
+ dp->x = dx;
+ dp->y = dy;
+ dp->gridx = gridx;
+ dp->gridy = gridy;
+
+ if ((dy >= drect->y1) &&
+ (dx >= drect->x1) &&
+ (dy <= drect->y2) &&
+ (dx <= drect->x2)) {
+ dp->next = node->taps;
+ node->taps = dp;
+ }
+ else {
+ dp->next = node->extend;
+ node->extend = dp;
+ }
+ gridy++;
+ }
+ gridx++;
+ }
+ }
+ node->netnum = net->netnum;
+ g->netnum[i] = net->netnum;
+ g->noderec[i] = node;
+ node->netname = net->netname;
+ node->next = net->netnodes;
+ net->netnodes = node;
+ break;
+ }
+ }
+ if (i < gateginfo->nodes) return;
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadNets --
+ *
+ * Read a NETS or SPECIALNETS section from a DEF file.
+ *
+ * Results:
+ * Return the total number of fixed or cover nets,
+ * excluding power and ground nets. This gives the
+ * base number of nets to be copied verbatim from
+ * input to output (used only for SPECIALNETS, as
+ * regular nets are tracked with the NET_IGNORED flag).
+ *
+ * Side Effects:
+ * Many. Networks are created, and geometry may be
+ * painted into the database top-level cell.
+ *
+ *------------------------------------------------------------
+ */
+
+enum def_net_keys {DEF_NET_START = 0, DEF_NET_END};
+enum def_netprop_keys {
+ DEF_NETPROP_USE = 0, DEF_NETPROP_ROUTED, DEF_NETPROP_FIXED,
+ DEF_NETPROP_COVER, DEF_NETPROP_SHAPE, DEF_NETPROP_SOURCE,
+ DEF_NETPROP_WEIGHT, DEF_NETPROP_PROPERTY};
+
+static int
+DefReadNets(FILE *f, char *sname, float oscale, char special, int total)
+{
+ char *token;
+ int keyword, subkey;
+ int i, processed = 0;
+ int nodeidx;
+ int fixed = 0;
+ char instname[MAX_NAME_LEN], pinname[MAX_NAME_LEN];
+ u_char is_new;
+
+ NET net;
+ int netidx;
+ NODE node;
+
+ static char *net_keys[] = {
+ "-",
+ "END",
+ NULL
+ };
+
+ static char *net_property_keys[] = {
+ "USE",
+ "ROUTED",
+ "FIXED",
+ "COVER",
+ "SHAPE",
+ "SOURCE",
+ "WEIGHT",
+ "PROPERTY",
+ NULL
+ };
+
+ if (Numnets == 0)
+ {
+ // Initialize net and node records
+ netidx = 0;
+ Nlnets = (NET *)malloc(total * sizeof(NET));
+ for (i = 0; i < total; i++) Nlnets[i] = NULL;
+ }
+ else {
+ netidx = Numnets;
+ Nlnets = (NET *)realloc(Nlnets, (Numnets + total) * sizeof(NET));
+ for (i = Numnets; i < (Numnets + total); i++) Nlnets[i] = NULL;
+ }
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, net_keys);
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in NET "
+ "definition; ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+
+ switch (keyword)
+ {
+ case DEF_NET_START:
+
+ /* Get net name */
+ token = LefNextToken(f, TRUE);
+ net = DefFindNet(token);
+
+ if (net == NULL) {
+ net = (NET)malloc(sizeof(struct net_));
+ Nlnets[Numnets++] = net;
+ net->numnodes = 0;
+ net->netname = strdup(token);
+ net->netnodes = (NODE)NULL;
+ net->Flags = (special) ? NET_SPECIAL : 0;
+
+ /* Check for backslash-escape names modified by other tools */
+ /* (e.g., vlog2Cel) which replace the trailing space with a */
+ /* backslash, making the name verilog-incompatible. */
+
+ if (*net->netname == '\\') {
+ char *sptr, *bptr;
+ sptr = strchr(net->netname, ' ');
+ if (sptr == NULL) {
+ bptr = strrchr(net->netname + 1, '\\');
+ if (bptr != NULL) *bptr = ' ';
+ }
+ }
+
+ net->netnum = netidx++;
+ DefHashNet(net);
+
+ nodeidx = 0;
+ is_new = TRUE;
+ }
+ else {
+ nodeidx = net->numnodes;
+ is_new = FALSE;
+ }
+
+ /* Update the record of the number of nets processed */
+ /* and spit out a message for every 5% finished. */
+
+ processed++;
+
+ /* Get next token; will be '(' if this is a netlist */
+ token = LefNextToken(f, TRUE);
+
+ /* Process all properties */
+ while (token && (*token != ';'))
+ {
+ /* Find connections for the net */
+ if (*token == '(')
+ {
+ token = LefNextToken(f, TRUE); /* get pin or gate */
+ strcpy(instname, token);
+ token = LefNextToken(f, TRUE); /* get node name */
+
+ if (!strcasecmp(instname, "pin")) {
+ strcpy(instname, token);
+ strcpy(pinname, "pin");
+ }
+ else
+ strcpy(pinname, token);
+
+ node = (NODE)calloc(1, sizeof(struct node_));
+ node->nodenum = nodeidx++;
+ DefReadGatePin(net, node, instname, pinname);
+
+ token = LefNextToken(f, TRUE); /* should be ')' */
+
+ continue;
+ }
+ else if (*token != '+')
+ {
+ token = LefNextToken(f, TRUE); /* Not a property */
+ continue; /* Ignore it, whatever it is */
+ }
+ else
+ token = LefNextToken(f, TRUE);
+
+ subkey = Lookup(token, net_property_keys);
+ if (subkey < 0)
+ {
+ LefError(DEF_WARNING, "Unknown net property \"%s\" in "
+ "NET definition; ignoring.\n", token);
+ continue;
+ }
+ switch (subkey)
+ {
+ case DEF_NETPROP_USE:
+ /* Presently, we ignore this */
+ break;
+ case DEF_NETPROP_SHAPE:
+ /* Ignore this too, along with the next keyword */
+ token = LefNextToken(f, TRUE);
+ break;
+ case DEF_NETPROP_FIXED:
+ case DEF_NETPROP_COVER:
+ /* Read in fixed nets like regular nets but mark
+ * them as NET_IGNORED. HOWEVER, if the net
+ * already exists and is not marked NET_IGNORED,
+ * then don't force it to be ignored. That is
+ * particularly an issue for a net like power or
+ * ground, which may need to be routed like a
+ * regular net but also has fixed portions. */
+ if (is_new) {
+ fixed++;
+ }
+ // fall through
+ case DEF_NETPROP_ROUTED:
+ // Read in the route; qrouter now takes
+ // responsibility for this route.
+ while (token && (*token != ';'))
+ token = DefAddRoutes(f, oscale, net, special);
+ // Treat power and ground nets in specialnets as fixed
+ if (subkey == DEF_NETPROP_ROUTED && special == (char)1)
+ fixed++;
+ break;
+ }
+ }
+ break;
+
+ case DEF_NET_END:
+ if (!LefParseEndStatement(f, sname))
+ {
+ LefError(DEF_ERROR, "Net END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == DEF_NET_END) break;
+ }
+
+ // Set the number of nodes per net for each node on the net
+
+ if (special == FALSE) {
+
+ // Fill in the netnodes list for each net, needed for checking
+ // for isolated routed groups within a net.
+
+ for (i = 0; i < Numnets; i++) {
+ net = Nlnets[i];
+ for (node = net->netnodes; node; node = node->next)
+ net->numnodes++;
+ for (node = net->netnodes; node; node = node->next)
+ node->numnodes = net->numnodes;
+ }
+ }
+
+ if (processed == total) {
+ if (Verbose > 0)
+ fprintf(stdout, " Processed %d%s nets total.\n", processed,
+ (special) ? " special" : "");
+ }
+ else
+ LefError(DEF_WARNING, "Warning: Number of nets read (%d) does not match "
+ "the number declared (%d).\n", processed, total);
+ return fixed;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadUseLocation --
+ *
+ * Read location and orientation of a cell use
+ * Syntax: ( X Y ) O
+ *
+ * Results:
+ * 0 on success, -1 on failure
+ *
+ * Side Effects:
+ * GATE definition for the use has the placedX, placedY,
+ * and orient values filled.
+ *------------------------------------------------------------
+ */
+enum def_orient {DEF_NORTH, DEF_SOUTH, DEF_EAST, DEF_WEST,
+ DEF_FLIPPED_NORTH, DEF_FLIPPED_SOUTH, DEF_FLIPPED_EAST,
+ DEF_FLIPPED_WEST};
+
+static int
+DefReadLocation(gate, f, oscale)
+ GATE gate;
+ FILE *f;
+ float oscale;
+{
+ int keyword;
+ char *token;
+ float x, y;
+ char mxflag, myflag;
+
+ static char *orientations[] = {
+ "N", "S", "E", "W", "FN", "FS", "FE", "FW"
+ };
+ static int oflags[] = {
+ RN, RS, RE, RW, RN | RF, RS | RF, RE | RF, RW | RF
+ };
+
+ token = LefNextToken(f, TRUE);
+ if (*token != '(') goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%f", &x) != 1) goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%f", &y) != 1) goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (*token != ')') goto parse_error;
+ token = LefNextToken(f, TRUE);
+
+ keyword = Lookup(token, orientations);
+ if (keyword < 0)
+ {
+ LefError(DEF_ERROR, "Unknown macro orientation \"%s\".\n", token);
+ return -1;
+ }
+
+ mxflag = myflag = (char)0;
+
+ switch (keyword)
+ {
+ case DEF_NORTH:
+ break;
+ case DEF_SOUTH:
+ mxflag = 1;
+ myflag = 1;
+ break;
+ case DEF_FLIPPED_NORTH:
+ mxflag = 1;
+ break;
+ case DEF_FLIPPED_SOUTH:
+ myflag = 1;
+ break;
+ }
+
+ if (gate) {
+ gate->placedX = x / oscale;
+ gate->placedY = y / oscale;
+ gate->orient = MNONE;
+ if (mxflag) gate->orient |= MX;
+ if (myflag) gate->orient |= MY;
+ gate->orient |= oflags[keyword];
+ }
+ return 0;
+
+parse_error:
+ LefError(DEF_ERROR, "Cannot parse location: must be ( X Y ) orient\n");
+ return -1;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadPins --
+ *
+ * Read a PINS section from a DEF file.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Generates paint and labels in the layout.
+ *
+ *------------------------------------------------------------
+ */
+
+enum def_pins_keys {DEF_PINS_START = 0, DEF_PINS_END};
+enum def_pins_prop_keys {
+ DEF_PINS_PROP_NET = 0, DEF_PINS_PROP_DIR,
+ DEF_PINS_PROP_LAYER, DEF_PINS_PROP_PLACED,
+ DEF_PINS_PROP_USE, DEF_PINS_PROP_FIXED,
+ DEF_PINS_PROP_COVER};
+
+static void
+DefReadPins(FILE *f, char *sname, float oscale, int total)
+{
+ char *token;
+ char pinname[MAX_NAME_LEN];
+ int keyword, subkey;
+ int processed = 0;
+ DSEG currect, drect;
+ GATE gate;
+ int curlayer;
+ double hwidth;
+ u_char pin_use;
+
+ static char *pin_keys[] = {
+ "-",
+ "END",
+ NULL
+ };
+
+ static char *pin_property_keys[] = {
+ "NET",
+ "DIRECTION",
+ "LAYER",
+ "PLACED",
+ "USE",
+ "FIXED",
+ "COVER",
+ NULL
+ };
+
+ static char *pin_classes[] = {
+ "DEFAULT",
+ "INPUT",
+ "OUTPUT TRISTATE",
+ "OUTPUT",
+ "INOUT",
+ "FEEDTHRU",
+ NULL
+ };
+
+ static char *pin_uses[] = {
+ "DEFAULT",
+ "SIGNAL",
+ "ANALOG",
+ "POWER",
+ "GROUND",
+ "CLOCK",
+ "TIEOFF",
+ "SCAN",
+ "RESET",
+ NULL
+ };
+
+ pin_use = PORT_USE_DEFAULT;
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, pin_keys);
+
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in PINS "
+ "definition; ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case DEF_PINS_START: /* "-" keyword */
+
+ /* Update the record of the number of pins */
+ /* processed and spit out a message for every 5% done. */
+
+ processed++;
+
+ /* Get pin name */
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%2047s", pinname) != 1)
+ {
+ LefError(DEF_ERROR, "Bad pin statement: Need pin name\n");
+ LefEndStatement(f);
+ break;
+ }
+
+ /* Create the pin record */
+ gate = (GATE)malloc(sizeof(struct gate_));
+ gate->gatetype = PinMacro;
+ gate->gatename = NULL; /* Use NET, but if none, use */
+ /* the pin name, set at end. */
+ gate->width = gate->height = 0;
+ curlayer = -1;
+
+ /* Pin record has one node; allocate memory for it */
+ gate->taps = (DSEG *)malloc(sizeof(DSEG));
+ gate->noderec = (NODE *)malloc(sizeof(NODE));
+ gate->direction = (u_char *)malloc(sizeof(u_char));
+ gate->area = (float *)malloc(sizeof(float));
+ gate->netnum = (int *)malloc(sizeof(int));
+ gate->node = (char **)malloc(sizeof(char *));
+ gate->taps[0] = NULL;
+ gate->noderec[0] = NULL;
+ gate->netnum[0] = -1;
+ gate->node[0] = NULL;
+ gate->direction[0] = PORT_CLASS_DEFAULT;
+ gate->area[0] = 0.0;
+ gate->clientdata = (void *)NULL;
+
+ /* Now do a search through the line for "+" entries */
+ /* And process each. */
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ if (*token == ';') break;
+ if (*token != '+') continue;
+
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, pin_property_keys);
+ if (subkey < 0)
+ {
+ LefError(DEF_WARNING, "Unknown pin property \"%s\" in "
+ "PINS definition; ignoring.\n", token);
+ continue;
+ }
+ switch (subkey)
+ {
+ case DEF_PINS_PROP_NET:
+ /* Get the net name */
+ token = LefNextToken(f, TRUE);
+ gate->gatename = strdup(token);
+ gate->node[0] = strdup(token);
+ break;
+ case DEF_PINS_PROP_DIR:
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, pin_classes);
+ if (subkey < 0)
+ LefError(DEF_ERROR, "Unknown pin class %s\n", token);
+ else
+ gate->direction[0] = subkey;
+ break;
+ case DEF_PINS_PROP_LAYER:
+ curlayer = LefReadLayer(f, FALSE);
+ currect = LefReadRect(f, curlayer, oscale);
+ /* Warn if pin is on layer above routing layer limit? */
+ if (currect) {
+ gate->width = currect->x2 - currect->x1;
+ gate->height = currect->y2 - currect->y1;
+ }
+ break;
+ case DEF_PINS_PROP_USE:
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, pin_uses);
+ if (subkey < 0)
+ LefError(DEF_ERROR, "Unknown pin use %s\n", token);
+ else
+ pin_use = subkey;
+ break;
+ case DEF_PINS_PROP_PLACED:
+ case DEF_PINS_PROP_FIXED:
+ case DEF_PINS_PROP_COVER:
+ DefReadLocation(gate, f, oscale);
+ break;
+ }
+ }
+
+ /* If no NET was declared for pin, use pinname */
+ if (gate->gatename == NULL)
+ gate->gatename = strdup(pinname);
+
+ /* Make sure pin is at least the size of the route layer */
+ drect = (DSEG)malloc(sizeof(struct dseg_));
+ gate->taps[0] = drect;
+ drect->next = (DSEG)NULL;
+
+ hwidth = LefGetRouteWidth(curlayer);
+ if (gate->width < hwidth) gate->width = hwidth;
+ if (gate->height < hwidth) gate->height = hwidth;
+ hwidth /= 2.0;
+ drect->x1 = gate->placedX - hwidth;
+ drect->y1 = gate->placedY - hwidth;
+ drect->x2 = gate->placedX + hwidth;
+ drect->y2 = gate->placedY + hwidth;
+ drect->layer = curlayer;
+ gate->obs = (DSEG)NULL;
+ gate->nodes = 1;
+ gate->next = Nlgates;
+ gate->last = (GATE)NULL;
+ if (Nlgates) Nlgates->last = gate;
+ Nlgates = gate;
+ Numpins++;
+
+ // Used by Tcl version of qrouter
+ DefHashInstance(gate);
+
+ break;
+
+ case DEF_PINS_END:
+ if (!LefParseEndStatement(f, sname))
+ {
+ LefError(DEF_ERROR, "Pins END statement missing.\n");
+ keyword = -1;
+ }
+ if (pin_use != PORT_USE_DEFAULT && gate->direction[0] ==
+ PORT_CLASS_DEFAULT)
+ {
+ /* Derive pin use from pin class, if needed */
+ switch (pin_use) {
+ case PORT_USE_SIGNAL:
+ case PORT_USE_RESET:
+ case PORT_USE_CLOCK:
+ case PORT_USE_SCAN:
+ gate->direction[0] = PORT_CLASS_INPUT;
+ break;
+
+ case PORT_USE_POWER:
+ case PORT_USE_GROUND:
+ case PORT_USE_TIEOFF:
+ case PORT_USE_ANALOG:
+ gate->direction[0] = PORT_CLASS_BIDIRECTIONAL;
+ break;
+ }
+ }
+ break;
+ }
+ if (keyword == DEF_PINS_END) break;
+ }
+
+ if (processed == total) {
+ if (Verbose > 0)
+ fprintf(stdout, " Processed %d pins total.\n", processed);
+ }
+ else
+ LefError(DEF_WARNING, "Warning: Number of pins read (%d) does not match "
+ "the number declared (%d).\n", processed, total);
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadVias --
+ *
+ * Read a VIAS section from a DEF file.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Technically, this routine should be creating a cell for
+ * each defined via. For now, it just computes the bounding
+ * rectangle and layer.
+ *
+ *------------------------------------------------------------
+ */
+
+enum def_vias_keys {DEF_VIAS_START = 0, DEF_VIAS_END};
+enum def_vias_prop_keys {
+ DEF_VIAS_PROP_RECT = 0};
+
+static void
+DefReadVias(f, sname, oscale, total)
+ FILE *f;
+ char *sname;
+ float oscale;
+ int total;
+{
+ char *token;
+ char vianame[LEF_LINE_MAX];
+ int keyword, subkey;
+ int processed = 0;
+ int curlayer;
+ LefList lefl;
+
+ static char *via_keys[] = {
+ "-",
+ "END",
+ NULL
+ };
+
+ static char *via_property_keys[] = {
+ "RECT",
+ NULL
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, via_keys);
+
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in VIAS "
+ "definition; ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case DEF_VIAS_START: /* "-" keyword */
+
+ /* Update the record of the number of vias */
+ /* processed and spit out a message for every 5% done. */
+
+ processed++;
+
+ /* Get via name */
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%2047s", vianame) != 1)
+ {
+ LefError(DEF_ERROR, "Bad via statement: Need via name\n");
+ LefEndStatement(f);
+ break;
+ }
+ lefl = LefFindLayer(token);
+ if (lefl == NULL)
+ {
+ lefl = (LefList)calloc(1, sizeof(lefLayer));
+ lefl->type = -1;
+ lefl->obsType = -1;
+ lefl->lefClass = CLASS_VIA;
+ lefl->info.via.area.x1 = 0.0;
+ lefl->info.via.area.y1 = 0.0;
+ lefl->info.via.area.x2 = 0.0;
+ lefl->info.via.area.y2 = 0.0;
+ lefl->info.via.area.layer = -1;
+ lefl->info.via.cell = (GATE)NULL;
+ lefl->info.via.lr = (DSEG)NULL;
+ /* Note: "generated" flag only refers to vias that */
+ /* are internally generated by qrouter. All others */
+ /* in the DEF file are read/written verbatim. */
+ lefl->info.via.generated = FALSE;
+ lefl->lefName = strdup(token);
+
+ lefl->next = LefInfo;
+ LefInfo = lefl;
+ }
+ else
+ {
+ LefError(DEF_WARNING, "Warning: Composite via \"%s\" "
+ "redefined.\n", vianame);
+ lefl = LefRedefined(lefl, vianame);
+ }
+
+ /* Now do a search through the line for "+" entries */
+ /* And process each. */
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ if (*token == ';') break;
+ if (*token != '+') continue;
+
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, via_property_keys);
+ if (subkey < 0)
+ {
+ LefError(DEF_WARNING, "Unknown via property \"%s\" in "
+ "VIAS definition; ignoring.\n", token);
+ continue;
+ }
+ switch (subkey)
+ {
+ case DEF_VIAS_PROP_RECT:
+ curlayer = LefReadLayer(f, FALSE);
+ LefAddViaGeometry(f, lefl, curlayer, oscale);
+ break;
+ }
+ }
+ break;
+
+ case DEF_VIAS_END:
+ if (!LefParseEndStatement(f, sname))
+ {
+ LefError(DEF_ERROR, "Vias END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == DEF_VIAS_END) break;
+ }
+
+ if (processed == total) {
+ if (Verbose > 0)
+ fprintf(stdout, " Processed %d vias total.\n", processed);
+ }
+ else
+ LefError(DEF_WARNING, "Warning: Number of vias read (%d) does not match "
+ "the number declared (%d).\n", processed, total);
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadBlockages --
+ *
+ * Read a BLOCKAGES section from a DEF file.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * UserObs list is updated with the additional
+ * obstructions.
+ *
+ *------------------------------------------------------------
+ */
+
+enum def_block_keys {DEF_BLOCK_START = 0, DEF_BLOCK_END};
+
+static void
+DefReadBlockages(FILE *f, char *sname, float oscale, int total)
+{
+ char *token;
+ int keyword;
+ int processed = 0;
+ DSEG drect, rsrch;
+ LefList lefl;
+
+ static char *blockage_keys[] = {
+ "-",
+ "END",
+ NULL
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, blockage_keys);
+
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in BLOCKAGE "
+ "definition; ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case DEF_BLOCK_START: /* "-" keyword */
+
+ /* Update the record of the number of components */
+ /* processed and spit out a message for every 5% done. */
+
+ processed++;
+
+ /* Get layer name */
+ token = LefNextToken(f, TRUE);
+ lefl = LefFindLayer(token);
+ if (lefl != NULL)
+ {
+ drect = LefReadGeometry(NULL, f, oscale);
+ if (UserObs == NULL)
+ UserObs = drect;
+ else {
+ for (rsrch = UserObs; rsrch->next; rsrch = rsrch->next);
+ rsrch->next = drect;
+ }
+ }
+ else
+ {
+ LefError(DEF_ERROR, "Bad blockage statement: Need layer name\n");
+ LefEndStatement(f);
+ break;
+ }
+ break;
+
+ case DEF_BLOCK_END:
+ if (!LefParseEndStatement(f, sname))
+ {
+ LefError(DEF_ERROR, "Blockage END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == DEF_BLOCK_END) break;
+ }
+
+ if (processed == total) {
+ if (Verbose > 0)
+ fprintf(stdout, " Processed %d blockages total.\n", processed);
+ }
+ else
+ LefError(DEF_WARNING, "Warning: Number of blockages read (%d) does not match "
+ "the number declared (%d).\n", processed, total);
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefAddGateInstance --
+ *
+ * Add a gate instance to the list of instances and
+ * instance hash table. The instance is assumed to
+ * have records gatename, gatetype, placedX, and
+ * placedY already set. The gate macro is found from
+ * the gatetype record, and all information about the
+ * cell macro is copied to the instance record, with
+ * positions adjusted for the instance.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Many. Cell instances are created and added to
+ * the database.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+DefAddGateInstance(GATE gate)
+{
+ GATE gateginfo;
+ int i;
+ DSEG drect, newrect;
+ double tmp;
+
+ if (gate == NULL) return;
+ gateginfo = gate->gatetype;
+ if (gateginfo == NULL) return;
+
+ /* Process the gate */
+ gate->width = gateginfo->width;
+ gate->height = gateginfo->height;
+ gate->nodes = gateginfo->nodes;
+ gate->obs = (DSEG)NULL;
+
+ gate->taps = (DSEG *)malloc(gate->nodes * sizeof(DSEG));
+ gate->noderec = (NODE *)malloc(gate->nodes * sizeof(NODE));
+ gate->direction = (u_char *)malloc(gate->nodes * sizeof(u_char));
+ gate->area = (float *)malloc(gate->nodes * sizeof(float));
+ gate->netnum = (int *)malloc(gate->nodes * sizeof(int));
+ gate->node = (char **)malloc(gate->nodes * sizeof(char *));
+
+ /* Let the node names point to the master cell; */
+ /* this is just diagnostic; allows us, for */
+ /* instance, to identify vdd and gnd nodes, so */
+ /* we don't complain about them being */
+ /* disconnected. */
+
+ for (i = 0; i < gate->nodes; i++) {
+ gate->node[i] = gateginfo->node[i]; /* copy pointer */
+ gate->direction[i] = gateginfo->direction[i]; /* copy */
+ gate->area[i] = gateginfo->area[i];
+ gate->taps[i] = (DSEG)NULL;
+ gate->netnum[i] = 0; /* Until we read NETS */
+ gate->noderec[i] = NULL;
+
+ /* Make a copy of the gate nodes and adjust for */
+ /* instance position and number of layers */
+
+ for (drect = gateginfo->taps[i]; drect; drect = drect->next) {
+ newrect = (DSEG)malloc(sizeof(struct dseg_));
+ *newrect = *drect;
+ newrect->next = gate->taps[i];
+ gate->taps[i] = newrect;
+ }
+
+ for (drect = gate->taps[i]; drect; drect = drect->next) {
+ // handle offset from gate origin
+ drect->x1 -= gateginfo->placedX;
+ drect->x2 -= gateginfo->placedX;
+ drect->y1 -= gateginfo->placedY;
+ drect->y2 -= gateginfo->placedY;
+
+ // handle rotations and orientations here
+ if (gate->orient & MX) {
+ tmp = drect->x1;
+ drect->x1 = -drect->x2;
+ drect->x1 += gate->placedX + gateginfo->width;
+ drect->x2 = -tmp;
+ drect->x2 += gate->placedX + gateginfo->width;
+ }
+ else {
+ drect->x1 += gate->placedX;
+ drect->x2 += gate->placedX;
+ }
+ if (gate->orient & MY) {
+ tmp = drect->y1;
+ drect->y1 = -drect->y2;
+ drect->y1 += gate->placedY + gateginfo->height;
+ drect->y2 = -tmp;
+ drect->y2 += gate->placedY + gateginfo->height;
+ }
+ else {
+ drect->y1 += gate->placedY;
+ drect->y2 += gate->placedY;
+ }
+ }
+ }
+
+ /* Make a copy of the gate obstructions and adjust */
+ /* for instance position */
+ for (drect = gateginfo->obs; drect; drect = drect->next) {
+ newrect = (DSEG)malloc(sizeof(struct dseg_));
+ *newrect = *drect;
+ newrect->next = gate->obs;
+ gate->obs = newrect;
+ }
+
+ for (drect = gate->obs; drect; drect = drect->next) {
+ drect->x1 -= gateginfo->placedX;
+ drect->x2 -= gateginfo->placedX;
+ drect->y1 -= gateginfo->placedY;
+ drect->y2 -= gateginfo->placedY;
+
+ // handle rotations and orientations here
+ if (gate->orient & MX) {
+ tmp = drect->x1;
+ drect->x1 = -drect->x2;
+ drect->x1 += gate->placedX + gateginfo->width;
+ drect->x2 = -tmp;
+ drect->x2 += gate->placedX + gateginfo->width;
+ }
+ else {
+ drect->x1 += gate->placedX;
+ drect->x2 += gate->placedX;
+ }
+ if (gate->orient & MY) {
+ tmp = drect->y1;
+ drect->y1 = -drect->y2;
+ drect->y1 += gate->placedY + gateginfo->height;
+ drect->y2 = -tmp;
+ drect->y2 += gate->placedY + gateginfo->height;
+ }
+ else {
+ drect->y1 += gate->placedY;
+ drect->y2 += gate->placedY;
+ }
+ }
+ gate->next = Nlgates;
+ gate->last = (GATE)NULL;
+ if (Nlgates) Nlgates->last = gate;
+ Nlgates = gate;
+ Numgates++;
+
+ // Used by Tcl version of qrouter
+ DefHashInstance(gate);
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefReadComponents --
+ *
+ * Read a COMPONENTS section from a DEF file.
+ *
+ * Results:
+ * 0 on success, 1 on fatal error.
+ *
+ * Side Effects:
+ * Many. Cell instances are created and added to
+ * the database.
+ *
+ *------------------------------------------------------------
+ */
+
+enum def_comp_keys {DEF_COMP_START = 0, DEF_COMP_END};
+enum def_prop_keys {
+ DEF_PROP_FIXED = 0, DEF_PROP_COVER,
+ DEF_PROP_PLACED, DEF_PROP_UNPLACED,
+ DEF_PROP_SOURCE, DEF_PROP_WEIGHT, DEF_PROP_FOREIGN,
+ DEF_PROP_REGION, DEF_PROP_GENERATE, DEF_PROP_PROPERTY,
+ DEF_PROP_EEQMASTER};
+
+static int
+DefReadComponents(FILE *f, char *sname, float oscale, int total)
+{
+ GATE gateginfo;
+ GATE gate = NULL;
+ char *token;
+ char usename[512];
+ int keyword, subkey, i;
+ int processed = 0;
+ char OK;
+ int err_fatal = 0;
+
+ static char *component_keys[] = {
+ "-",
+ "END",
+ NULL
+ };
+
+ static char *property_keys[] = {
+ "FIXED",
+ "COVER",
+ "PLACED",
+ "UNPLACED",
+ "SOURCE",
+ "WEIGHT",
+ "FOREIGN",
+ "REGION",
+ "GENERATE",
+ "PROPERTY",
+ "EEQMASTER",
+ NULL
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, component_keys);
+
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in COMPONENT "
+ "definition; ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case DEF_COMP_START: /* "-" keyword */
+
+ /* Update the record of the number of components */
+ /* processed and spit out a message for every 5% done. */
+
+ processed++;
+
+ /* Get use and macro names */
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%511s", usename) != 1)
+ {
+ LefError(DEF_ERROR, "Bad component statement: Need use "
+ "and macro names\n");
+ LefEndStatement(f);
+ err_fatal++;
+ break;
+ }
+ token = LefNextToken(f, TRUE);
+
+ /* Find the corresponding macro */
+ OK = 0;
+ for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next) {
+ if (!strcasecmp(gateginfo->gatename, token)) {
+ OK = 1;
+ break;
+ }
+ }
+ if (!OK) {
+ LefError(DEF_ERROR, "Could not find a macro definition for \"%s\"\n",
+ token);
+ gate = NULL;
+ err_fatal++;
+ }
+ else {
+ gate = (GATE)malloc(sizeof(struct gate_));
+ gate->gatename = strdup(usename);
+ gate->gatetype = gateginfo;
+ gate->clientdata = (void *)NULL;
+ }
+
+ /* Now do a search through the line for "+" entries */
+ /* And process each. */
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ if (*token == ';') break;
+ if (*token != '+') continue;
+
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, property_keys);
+ if (subkey < 0)
+ {
+ LefError(DEF_WARNING, "Unknown component property \"%s\" in "
+ "COMPONENT definition; ignoring.\n", token);
+ continue;
+ }
+ switch (subkey)
+ {
+ case DEF_PROP_PLACED:
+ case DEF_PROP_UNPLACED:
+ case DEF_PROP_FIXED:
+ case DEF_PROP_COVER:
+ DefReadLocation(gate, f, oscale);
+ break;
+ case DEF_PROP_SOURCE:
+ case DEF_PROP_WEIGHT:
+ case DEF_PROP_FOREIGN:
+ case DEF_PROP_REGION:
+ case DEF_PROP_GENERATE:
+ case DEF_PROP_PROPERTY:
+ case DEF_PROP_EEQMASTER:
+ token = LefNextToken(f, TRUE);
+ break;
+ }
+ }
+ DefAddGateInstance(gate);
+ break;
+
+ case DEF_COMP_END:
+ if (!LefParseEndStatement(f, sname))
+ {
+ LefError(DEF_ERROR, "Component END statement missing.\n");
+ keyword = -1;
+ err_fatal++;
+ }
+
+ /* Finish final call by placing the cell use */
+ if ((total > 0) && (gate != NULL))
+ {
+ // Nothing to do. . . gate has already been placed in list.
+ gate = NULL;
+ }
+ break;
+ }
+ if (keyword == DEF_COMP_END) break;
+ }
+
+ if (processed == total) {
+ if (Verbose > 0)
+ fprintf(stdout, " Processed %d subcell instances total.\n", processed);
+ }
+ else
+ LefError(DEF_WARNING, "Warning: Number of subcells read (%d) does not match "
+ "the number declared (%d).\n", processed, total);
+ return err_fatal;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * DefRead --
+ *
+ * Read a .def file and parse die area, track positions,
+ * components, pins, and nets.
+ *
+ * Results:
+ * Returns the units scale, so the routed output can be
+ * scaled to match the DEF file header.
+ *
+ * Side Effects:
+ * Many.
+ *
+ *------------------------------------------------------------
+ */
+
+/* Enumeration of sections defined in DEF files */
+
+enum def_sections {DEF_VERSION = 0, DEF_NAMESCASESENSITIVE,
+ DEF_UNITS, DEF_DESIGN, DEF_REGIONS, DEF_ROW, DEF_TRACKS,
+ DEF_GCELLGRID, DEF_DIVIDERCHAR, DEF_BUSBITCHARS,
+ DEF_PROPERTYDEFINITIONS, DEF_DEFAULTCAP, DEF_TECHNOLOGY,
+ DEF_HISTORY, DEF_DIEAREA, DEF_COMPONENTS, DEF_VIAS,
+ DEF_PINS, DEF_PINPROPERTIES, DEF_SPECIALNETS,
+ DEF_NETS, DEF_IOTIMINGS, DEF_SCANCHAINS, DEF_BLOCKAGES,
+ DEF_CONSTRAINTS, DEF_GROUPS, DEF_EXTENSION,
+ DEF_END};
+
+int
+DefRead(char *inName, float *retscale)
+{
+ FILE *f;
+ char filename[256];
+ char namepos[32];
+ char *token;
+ int keyword, dscale, total;
+ int curlayer = -1, channels;
+ int i;
+ int err_fatal = 0;
+ float oscale;
+ double start, step;
+ double llx, lly, urx, ury, locpitch;
+ double dXlowerbound, dYlowerbound, dXupperbound, dYupperbound;
+ char corient = '.';
+ DSEG diearea;
+ ROW newrow;
+
+ static char *orientations[] = {
+ "N", "S", "E", "W", "FN", "FS", "FE", "FW"
+ };
+ static int oflags[] = {
+ RN, RS, RE, RW, RN | RF, RS | RF, RE | RF, RW | RF
+ };
+
+ static char *sections[] = {
+ "VERSION",
+ "NAMESCASESENSITIVE",
+ "UNITS",
+ "DESIGN",
+ "REGIONS",
+ "ROW",
+ "TRACKS",
+ "GCELLGRID",
+ "DIVIDERCHAR",
+ "BUSBITCHARS",
+ "PROPERTYDEFINITIONS",
+ "DEFAULTCAP",
+ "TECHNOLOGY",
+ "HISTORY",
+ "DIEAREA",
+ "COMPONENTS",
+ "VIAS",
+ "PINS",
+ "PINPROPERTIES",
+ "SPECIALNETS",
+ "NETS",
+ "IOTIMINGS",
+ "SCANCHAINS",
+ "BLOCKAGES",
+ "CONSTRAINTS",
+ "GROUPS",
+ "BEGINEXT",
+ "END",
+ NULL
+ };
+
+ if (!strrchr(inName, '.'))
+ sprintf(filename, "%s.def", inName);
+ else
+ strcpy(filename, inName);
+
+ f = fopen(filename, "r");
+
+ if (f == NULL)
+ {
+ fprintf(stderr, "Cannot open input file: ");
+ perror(filename);
+ *retscale = (float)0.0;
+ return 1;
+ }
+
+ /* Initialize */
+
+ if (Verbose > 0) {
+ fprintf(stdout, "Reading DEF data from file %s.\n", filename);
+ fflush(stdout);
+ }
+
+ oscale = 1;
+ lefCurrentLine = 0;
+
+ DefHashInit();
+
+ /* Read file contents */
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, sections);
+ if (keyword < 0)
+ {
+ LefError(DEF_WARNING, "Unknown keyword \"%s\" in DEF file; "
+ "ignoring.\n", token);
+ LefEndStatement(f);
+ continue;
+ }
+ if (keyword != DEF_TRACKS) corient = '.';
+
+ switch (keyword)
+ {
+ case DEF_VERSION:
+ LefEndStatement(f);
+ break;
+ case DEF_NAMESCASESENSITIVE:
+ LefEndStatement(f);
+ break;
+ case DEF_TECHNOLOGY:
+ token = LefNextToken(f, TRUE);
+ if (Verbose > 0)
+ fprintf(stdout, "Diagnostic: DEF file technology: \"%s\"\n",
+ token);
+ LefEndStatement(f);
+ break;
+ case DEF_REGIONS:
+ LefSkipSection(f, sections[DEF_REGIONS]);
+ break;
+ case DEF_DESIGN:
+ token = LefNextToken(f, TRUE);
+ if (Verbose > 0)
+ fprintf(stdout, "Diagnostic: Design name: \"%s\"\n", token);
+ DEFDesignName = strdup(token);
+ LefEndStatement(f);
+ break;
+ case DEF_UNITS:
+ token = LefNextToken(f, TRUE);
+ token = LefNextToken(f, TRUE);
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &dscale) != 1)
+ {
+ LefError(DEF_ERROR, "Invalid syntax for UNITS statement.\n");
+ LefError(DEF_WARNING, "Assuming default value of 100\n");
+ dscale = 100;
+ }
+ /* We don't care if the scale is 100, 200, 1000, or 2000. */
+ /* Do we need to deal with numeric roundoff issues? */
+ oscale *= (float)dscale;
+ LefEndStatement(f);
+ break;
+ case DEF_ROW:
+ newrow = (ROW)malloc(sizeof(struct row_));
+ token = LefNextToken(f, TRUE);
+ newrow->rowname = strdup(token);
+ token = LefNextToken(f, TRUE);
+ newrow->sitename = strdup(token);
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->x);
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->y);
+ token = LefNextToken(f, TRUE);
+ keyword = Lookup(token, orientations);
+ if (keyword < 0)
+ newrow->orient = 0;
+ else
+ newrow->orient = oflags[keyword];
+ token = LefNextToken(f, TRUE); /* skip "DO" */
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->xnum);
+ token = LefNextToken(f, TRUE); /* skip "BY" */
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->ynum);
+ token = LefNextToken(f, TRUE); /* skip "STEP" */
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->xstep);
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%d", &newrow->ystep);
+ sprintf(namepos, "%d", newrow->y);
+ HashPtrInstall(namepos, newrow, &RowTable);
+ LefEndStatement(f);
+ break;
+ case DEF_TRACKS:
+ token = LefNextToken(f, TRUE);
+ if (strlen(token) != 1) {
+ LefError(DEF_ERROR, "Problem parsing track orientation (X or Y).\n");
+ }
+ corient = tolower(token[0]); // X or Y
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%lg", &start) != 1) {
+ LefError(DEF_ERROR, "Problem parsing track start position.\n");
+ err_fatal++;
+ }
+ token = LefNextToken(f, TRUE);
+ if (strcmp(token, "DO")) {
+ LefError(DEF_ERROR, "TRACKS missing DO loop.\n");
+ err_fatal++;
+ }
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &channels) != 1) {
+ LefError(DEF_ERROR, "Problem parsing number of track channels.\n");
+ err_fatal++;
+ }
+ token = LefNextToken(f, TRUE);
+ if (strcmp(token, "STEP")) {
+ LefError(DEF_ERROR, "TRACKS missing STEP size.\n");
+ err_fatal++;
+ }
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%lg", &step) != 1) {
+ LefError(DEF_ERROR, "Problem parsing track step size.\n");
+ err_fatal++;
+ }
+ token = LefNextToken(f, TRUE);
+ if (!strcmp(token, "LAYER")) {
+ curlayer = LefReadLayer(f, FALSE);
+ }
+ if (corient == 'x') {
+ locpitch = step / oscale;
+ if ((PitchX == 0.0) || ((locpitch < PitchX) && (locpitch != 0)))
+ PitchX = locpitch;
+ llx = start;
+ urx = start + step * channels;
+ if ((llx / oscale) < Xlowerbound)
+ Xlowerbound = llx / oscale;
+ if ((urx / oscale) > Xupperbound)
+ Xupperbound = urx / oscale;
+ }
+ else {
+ locpitch = step / oscale;
+ if ((PitchY == 0.0) || ((locpitch < PitchY) && (locpitch != 0)))
+ PitchY = locpitch;
+ lly = start;
+ ury = start + step * channels;
+ if ((lly / oscale) < Ylowerbound)
+ Ylowerbound = lly / oscale;
+ if ((ury / oscale) > Yupperbound)
+ Yupperbound = ury / oscale;
+ }
+ LefEndStatement(f);
+ break;
+ case DEF_GCELLGRID:
+ LefEndStatement(f);
+ break;
+ case DEF_DIVIDERCHAR:
+ LefEndStatement(f);
+ break;
+ case DEF_BUSBITCHARS:
+ LefEndStatement(f);
+ break;
+ case DEF_HISTORY:
+ LefEndStatement(f);
+ break;
+ case DEF_DIEAREA:
+ diearea = LefReadRect(f, 0, oscale); // no current layer, use 0
+ dXlowerbound = diearea->x1;
+ dYlowerbound = diearea->y1;
+ dXupperbound = diearea->x2;
+ dYupperbound = diearea->y2;
+ /* Seed actual lower/upper bounds with the midpoint */
+ Xlowerbound = (diearea->x1 + diearea->x2) / 2;
+ Ylowerbound = (diearea->y1 + diearea->y2) / 2;
+ Xupperbound = Xlowerbound;
+ Yupperbound = Ylowerbound;
+ LefEndStatement(f);
+ break;
+ case DEF_PROPERTYDEFINITIONS:
+ LefSkipSection(f, sections[DEF_PROPERTYDEFINITIONS]);
+ break;
+ case DEF_DEFAULTCAP:
+ LefSkipSection(f, sections[DEF_DEFAULTCAP]);
+ break;
+ case DEF_COMPONENTS:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ err_fatal += DefReadComponents(f, sections[DEF_COMPONENTS], oscale, total);
+ break;
+ case DEF_BLOCKAGES:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ DefReadBlockages(f, sections[DEF_BLOCKAGES], oscale, total);
+ break;
+ case DEF_VIAS:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ DefReadVias(f, sections[DEF_VIAS], oscale, total);
+ break;
+ case DEF_PINS:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ DefReadPins(f, sections[DEF_PINS], oscale, total);
+ break;
+ case DEF_PINPROPERTIES:
+ LefSkipSection(f, sections[DEF_PINPROPERTIES]);
+ break;
+ case DEF_SPECIALNETS:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ numSpecial = DefReadNets(f, sections[DEF_SPECIALNETS], oscale, TRUE,
+ total);
+ break;
+ case DEF_NETS:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%d", &total) != 1) total = 0;
+ LefEndStatement(f);
+ DefReadNets(f, sections[DEF_NETS], oscale, FALSE, total);
+ break;
+ case DEF_IOTIMINGS:
+ LefSkipSection(f, sections[DEF_IOTIMINGS]);
+ break;
+ case DEF_SCANCHAINS:
+ LefSkipSection(f, sections[DEF_SCANCHAINS]);
+ break;
+ case DEF_CONSTRAINTS:
+ LefSkipSection(f, sections[DEF_CONSTRAINTS]);
+ break;
+ case DEF_GROUPS:
+ LefSkipSection(f, sections[DEF_GROUPS]);
+ break;
+ case DEF_EXTENSION:
+ LefSkipSection(f, sections[DEF_EXTENSION]);
+ break;
+ case DEF_END:
+ if (!LefParseEndStatement(f, "DESIGN"))
+ {
+ LefError(DEF_ERROR, "END statement out of context.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == DEF_END) break;
+ }
+ if (Verbose > 0)
+ fprintf(stdout, "DEF read: Processed %d lines.\n", lefCurrentLine);
+ LefError(DEF_ERROR, NULL); /* print statement of errors, if any, and reset */
+
+ /* If there were no TRACKS statements, then use the DIEAREA */
+ if (Xlowerbound == Xupperbound) {
+ Xlowerbound = dXlowerbound;
+ Xupperbound = dXupperbound;
+ }
+ if (Ylowerbound == Yupperbound) {
+ Ylowerbound = dYlowerbound;
+ Yupperbound = dYupperbound;
+ }
+
+ /* Cleanup */
+
+ if (f != NULL) fclose(f);
+ *retscale = oscale;
+ return err_fatal;
+}
diff --git a/src/readdef.h b/src/readdef.h
new file mode 100644
index 0000000..2a1da5d
--- /dev/null
+++ b/src/readdef.h
@@ -0,0 +1,31 @@
+/*
+ * def.h --
+ *
+ * This file includes the DEF I/O functions
+ *
+ */
+
+#ifndef _DEFINT_H
+#define _DEFINT_H
+
+#include "lef.h"
+
+extern int DefRead(char *inName, float *);
+
+extern int Numnets;
+extern int Numgates;
+extern int Numpins;
+extern int numSpecial;
+
+extern GATE DefFindGate(char *name);
+extern NET DefFindNet(char *name);
+extern ROW DefFindRow(int yval);
+extern ROW DefLowestRow();
+extern void DefAddGateInstance(GATE gate);
+extern char *DefDesign();
+
+/* External access to hash tables for recursion functions */
+extern struct hashtable InstanceTable;
+extern struct hashtable NetTable;
+
+#endif /* _DEFINT_H */
diff --git a/src/readlef.c b/src/readlef.c
new file mode 100644
index 0000000..21a8b1d
--- /dev/null
+++ b/src/readlef.c
@@ -0,0 +1,3436 @@
+/*
+ * lef.c --
+ *
+ * This module incorporates the LEF/DEF format for standard-cell routing
+ * route.
+ *
+ * Version 0.1 (September 26, 2003): LEF input handling. Includes creation
+ * of cells from macro statements, handling of pins, ports, obstructions, and
+ * associated geometry.
+ *
+ * Written by Tim Edwards, Open Circuit Design
+ * Modified June 2011 for use with qrouter.
+ * Modified November 2018 for use with qflow.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <math.h>
+
+#include "hash.h"
+#include "readlef.h"
+
+/*----------------------------------------------------------------------*/
+/* Global variables */
+/*----------------------------------------------------------------------*/
+
+GATE GateInfo = NULL; // standard cell macro information
+u_char Verbose = 0;
+char delimiter; // opening bus delimiter;
+
+struct hashtable MacroTable;
+
+/*----------------------------------------------------------------------*/
+
+/* Current line number for reading */
+int lefCurrentLine = 0;
+
+/* Information about routing layers */
+LefList LefInfo = NULL;
+
+/* Information about what vias to use */
+LinkedStringPtr AllowedVias = NULL;
+
+/* Gate information is in the linked list GateInfo, imported */
+
+/*
+ *---------------------------------------------------------
+ * Initialize hash table of cells
+ *---------------------------------------------------------
+ */
+
+static void
+LefHashInit(void)
+{
+ /* Initialize the macro hash table */
+ InitializeHashTable(&MacroTable, SMALLHASHSIZE);
+}
+
+/*
+ *---------------------------------------------------------
+ * Add macro to Lef cell macro hash table
+ *---------------------------------------------------------
+ */
+
+static void
+LefHashMacro(GATE gateginfo)
+{
+ HashPtrInstall(gateginfo->gatename, gateginfo, &MacroTable);
+}
+
+/*---------------------------------------------------------
+ * Lookup --
+ * Searches a table of strings to find one that matches a given
+ * string. It's useful mostly for command lookup.
+ *
+ * Only the portion of a string in the table up to the first
+ * blank character is considered significant for matching.
+ *
+ * Results:
+ * If str is the same as
+ * or an unambiguous abbreviation for one of the entries
+ * in table, then the index of the matching entry is returned.
+ * If str is not the same as any entry in the table, but
+ * an abbreviation for more than one entry,
+ * then -1 is returned. If str doesn't match any entry, then
+ * -2 is returned. Case differences are ignored.
+ *
+ * NOTE:
+ * Table entries need no longer be in alphabetical order
+ * and they need not be lower case. The irouter command parsing
+ * depends on these features.
+ *
+ * Side Effects:
+ * None.
+ *---------------------------------------------------------
+ */
+
+int
+Lookup(str, table)
+ char *str; /* Pointer to a string to be looked up */
+ char *(table[]); /* Pointer to an array of string pointers
+ * which are the valid commands.
+ * The end of
+ * the table is indicated by a NULL string.
+ */
+{
+ int match = -2; /* result, initialized to -2 = no match */
+ int pos;
+ int ststart = 0;
+
+ /* search for match */
+ for (pos=0; table[pos] != NULL; pos++)
+ {
+ char *tabc = table[pos];
+ char *strc = &(str[ststart]);
+ while (*strc!='\0' && *tabc!=' ' &&
+ ((*tabc==*strc) ||
+ (isupper(*tabc) && islower(*strc) && (tolower(*tabc)== *strc))||
+ (islower(*tabc) && isupper(*strc) && (toupper(*tabc)== *strc)) ))
+ {
+ strc++;
+ tabc++;
+ }
+
+
+ if (*strc=='\0')
+ {
+ /* entry matches */
+ if(*tabc==' ' || *tabc=='\0')
+ {
+ /* exact match - record it and terminate search */
+ match = pos;
+ break;
+ }
+ else if (match == -2)
+ {
+ /* inexact match and no previous match - record this one
+ * and continue search */
+ match = pos;
+ }
+ else
+ {
+ /* previous match, so string is ambiguous unless exact
+ * match exists. Mark ambiguous for now, and continue
+ * search.
+ */
+ match = -1;
+ }
+ }
+ }
+ return(match);
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * LookupFull --
+ *
+ * Look up a string in a table of pointers to strings. The last
+ * entry in the string table must be a NULL pointer.
+ * This is much simpler than Lookup() in that it does not
+ * allow abbreviations. It does, however, ignore case.
+ *
+ * Results:
+ * Index of the name supplied in the table, or -1 if the name
+ * is not found.
+ *
+ * Side effects:
+ * None.
+ *
+ * ----------------------------------------------------------------------------
+ */
+
+int
+LookupFull(name, table)
+ char *name;
+ char **table;
+{
+ char **tp;
+
+ for (tp = table; *tp; tp++)
+ {
+ if (strcmp(name, *tp) == 0)
+ return (tp - table);
+ else
+ {
+ char *sptr, *tptr;
+ for (sptr = name, tptr = *tp; ((*sptr != '\0') && (*tptr != '\0'));
+ sptr++, tptr++)
+ if (toupper(*sptr) != toupper(*tptr))
+ break;
+ if ((*sptr == '\0') && (*tptr == '\0'))
+ return (tp - table);
+ }
+ }
+
+ return (-1);
+}
+
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefNextToken --
+ *
+ * Move to the next token in the stream input.
+ * If "ignore_eol" is FALSE, then the end-of-line character
+ * "\n" will be returned as a token when encountered.
+ * Otherwise, end-of-line will be ignored.
+ *
+ * Results:
+ * Pointer to next token to parse
+ *
+ * Side Effects:
+ * May read a new line from the specified file.
+ *
+ * Warnings:
+ * The return result of LefNextToken will be overwritten by
+ * subsequent calls to LefNextToken if more than one line of
+ * input is parsed.
+ *
+ *------------------------------------------------------------
+ */
+
+char *
+LefNextToken(FILE *f, u_char ignore_eol)
+{
+ static char line[LEF_LINE_MAX + 2]; /* input buffer */
+ static char *nexttoken = NULL; /* pointer to next token */
+ static char *curtoken; /* pointer to current token */
+ static char eol_token='\n';
+
+ /* Read a new line if necessary */
+
+ if (nexttoken == NULL)
+ {
+ for(;;)
+ {
+ if (fgets(line, LEF_LINE_MAX + 1, f) == NULL) return NULL;
+ lefCurrentLine++;
+ curtoken = line;
+ while (isspace(*curtoken) && (*curtoken != '\n') && (*curtoken != '\0'))
+ curtoken++; /* skip leading whitespace */
+
+ if ((*curtoken != '#') && (*curtoken != '\n') && (*curtoken != '\0'))
+ {
+ nexttoken = curtoken;
+ break;
+ }
+ }
+ if (!ignore_eol)
+ return &eol_token;
+ }
+ else
+ curtoken = nexttoken;
+
+ /* Find the next token; set to NULL if none (end-of-line). */
+ /* Treat quoted material as a single token */
+
+ if (*nexttoken == '\"') {
+ nexttoken++;
+ while (((*nexttoken != '\"') || (*(nexttoken - 1) == '\\')) &&
+ (*nexttoken != '\0')) {
+ if (*nexttoken == '\n') {
+ if (fgets(nexttoken + 1, LEF_LINE_MAX -
+ (size_t)(nexttoken - line), f) == NULL)
+ return NULL;
+ }
+ nexttoken++; /* skip all in quotes (move past current token) */
+ }
+ if (*nexttoken == '\"')
+ nexttoken++;
+ }
+ else {
+ while (!isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
+ nexttoken++; /* skip non-whitespace (move past current token) */
+ }
+
+ /* Terminate the current token */
+ if (*nexttoken != '\0') *nexttoken++ = '\0';
+
+ while (isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n'))
+ nexttoken++; /* skip any whitespace */
+
+ if ((*nexttoken == '#') || (*nexttoken == '\n') || (*nexttoken == '\0'))
+ nexttoken = NULL;
+
+ return curtoken;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefError --
+ *
+ * Print an error message (via fprintf) giving the line
+ * number of the input file on which the error occurred.
+ *
+ * "type" should be either LEF_ERROR or LEF_WARNING (or DEF_*).
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Prints to the output (stderr).
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefError(int type, char *fmt, ...)
+{
+ static int fatal = 0;
+ static int nonfatal = 0;
+ char lefordef = 'L';
+ int errors;
+ va_list args;
+
+ if (Verbose == 0) return;
+
+ if ((type == DEF_WARNING) || (type == DEF_ERROR)) lefordef = 'D';
+
+ errors = fatal + nonfatal;
+ if (fmt == NULL) /* Special case: report any errors and reset */
+ {
+ if (errors > 0)
+ {
+ fprintf(stdout, "%cEF Read: encountered %d error%s and %d warning%s total.\n",
+ lefordef,
+ fatal, (fatal == 1) ? "" : "s",
+ nonfatal, (nonfatal == 1) ? "" : "s");
+ fatal = 0;
+ nonfatal = 0;
+ }
+ return;
+ }
+
+ if (errors < LEF_MAX_ERRORS)
+ {
+ fprintf(stderr, "%cEF Read, Line %d: ", lefordef, lefCurrentLine);
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fflush(stderr);
+ }
+ else if (errors == LEF_MAX_ERRORS)
+ fprintf(stderr, "%cEF Read: Further errors/warnings will not be reported.\n",
+ lefordef);
+
+ if ((type == LEF_ERROR) || (type == DEF_ERROR))
+ fatal++;
+ else if ((type == LEF_WARNING) || (type == DEF_WARNING))
+ nonfatal++;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefParseEndStatement --
+ *
+ * Check if the string passed in "lineptr" contains the
+ * appropriate matching keyword. Sections in LEF files
+ * should end with "END (keyword)" or "END". To check
+ * against the latter case, "match" should be NULL.
+ *
+ * Results:
+ * TRUE if the line matches the expected end statement,
+ * FALSE if not.
+ *
+ * Side effects:
+ * None.
+ *
+ *------------------------------------------------------------
+ */
+
+u_char
+LefParseEndStatement(FILE *f, char *match)
+{
+ char *token;
+ int keyword;
+ char *match_name[2];
+
+ match_name[0] = match;
+ match_name[1] = NULL;
+
+ token = LefNextToken(f, (match == NULL) ? FALSE : TRUE);
+ if (token == NULL)
+ {
+ LefError(LEF_ERROR, "Bad file read while looking for END statement\n");
+ return FALSE;
+ }
+
+ /* END or ENDEXT */
+ if ((*token == '\n') && (match == NULL)) return TRUE;
+
+ /* END <section_name> */
+ else {
+ keyword = LookupFull(token, match_name);
+ if (keyword == 0)
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefSkipSection --
+ *
+ * Skip to the "END" record of a LEF input section
+ * String "section" must follow the "END" statement in
+ * the file to be considered a match; however, if
+ * section = NULL, then "END" must have no strings
+ * following.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Reads input from the specified file. Prints an
+ * error message if the expected END record cannot
+ * be found.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefSkipSection(FILE *f, char *section)
+{
+ char *token;
+ int keyword;
+ static char *end_section[] = {
+ "END",
+ "ENDEXT",
+ NULL
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ if ((keyword = Lookup(token, end_section)) == 0)
+ {
+ if (LefParseEndStatement(f, section))
+ return;
+ }
+ else if (keyword == 1)
+ {
+ if (!strcmp(section, "BEGINEXT"))
+ return;
+ }
+ }
+
+ LefError(LEF_ERROR, "Section %s has no END record!\n", section);
+ return;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * lefFindCell --
+ *
+ * "name" is the name of the cell to search for.
+ * Returns the GATE entry for the cell from the GateInfo
+ * hash table.
+ *
+ *------------------------------------------------------------
+ */
+
+GATE
+lefFindCell(char *name)
+{
+ GATE gateginfo;
+
+ gateginfo = (GATE)HashLookup(name, &MacroTable);
+ return gateginfo;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefLower --
+ *
+ * Convert a token in a LEF or DEF file to all-lowercase.
+ *
+ *------------------------------------------------------------
+ */
+
+char *
+LefLower(char *token)
+{
+ char *tptr;
+
+ for (tptr = token; *tptr != '\0'; tptr++)
+ *tptr = tolower(*tptr);
+
+ return token;
+}
+
+LefList
+LefRedefined(LefList lefl, char *redefname)
+{
+ LefList slef, newlefl;
+ char *altName;
+ int records;
+ DSEG drect;
+
+ /* check if more than one entry points to the same */
+ /* lefLayer record. If so, we will also record the */
+ /* name of the first type that is not the same as */
+ /* "redefname". */
+
+ records = 0;
+ altName = NULL;
+
+ for (slef = LefInfo; slef; slef = slef->next) {
+ if (slef == lefl)
+ records++;
+ if (altName == NULL)
+ if (strcmp(slef->lefName, redefname))
+ altName = (char *)slef->lefName;
+ }
+ if (records == 1)
+ {
+ /* Only one name associated with the record, so */
+ /* just clear all the allocated information. */
+
+ while (lefl->info.via.lr) {
+ drect = lefl->info.via.lr->next;
+ free(lefl->info.via.lr);
+ lefl->info.via.lr = drect;
+ }
+ newlefl = lefl;
+ }
+ else
+ {
+ slef = LefFindLayer(redefname);
+
+ newlefl = (LefList)malloc(sizeof(lefLayer));
+ newlefl->lefName = strdup(newlefl->lefName);
+
+ newlefl->next = LefInfo;
+ LefInfo = newlefl;
+
+ /* If the canonical name of the original entry */
+ /* is "redefname", then change it. */
+
+ if (!strcmp(slef->lefName, redefname))
+ if (altName != NULL)
+ slef->lefName = altName;
+ }
+ newlefl->type = -1;
+ newlefl->obsType = -1;
+ newlefl->info.via.area.x1 = 0.0;
+ newlefl->info.via.area.x2 = 0.0;
+ newlefl->info.via.area.y1 = 0.0;
+ newlefl->info.via.area.y2 = 0.0;
+ newlefl->info.via.area.layer = -1;
+ newlefl->info.via.cell = (GATE)NULL;
+ newlefl->info.via.lr = (DSEG)NULL;
+
+ return newlefl;
+}
+
+/*
+ *------------------------------------------------------------
+ * Find a layer record in the list of layers
+ *------------------------------------------------------------
+ */
+
+LefList
+LefFindLayer(char *token)
+{
+ LefList lefl, rlefl;
+
+ if (token == NULL) return NULL;
+ rlefl = (LefList)NULL;
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (!strcmp(lefl->lefName, token)) {
+ rlefl = lefl;
+ break;
+ }
+ }
+ return rlefl;
+}
+
+/*
+ *------------------------------------------------------------
+ * Find a layer record in the list of layers, by layer number
+ *------------------------------------------------------------
+ */
+
+LefList
+LefFindLayerByNum(int layer)
+{
+ LefList lefl, rlefl;
+
+ rlefl = (LefList)NULL;
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->type == layer) {
+ rlefl = lefl;
+ break;
+ }
+ }
+ return rlefl;
+}
+
+/*
+ *------------------------------------------------------------
+ * Find a layer record in the list of layers, and return the
+ * layer number.
+ *------------------------------------------------------------
+ */
+
+int
+LefFindLayerNum(char *token)
+{
+ LefList lefl;
+
+ lefl = LefFindLayer(token);
+ if (lefl)
+ return lefl->type;
+ else
+ return -1;
+}
+
+/*
+ *---------------------------------------------------------------
+ * Find the maximum layer number defined by the LEF file
+ * This includes layer numbers assigned to both routes and
+ * via cut layers.
+ *---------------------------------------------------------------
+ */
+
+int
+LefGetMaxLayer(void)
+{
+ int maxlayer = -1;
+ LefList lefl;
+
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->type > maxlayer)
+ maxlayer = lefl->type;
+ }
+ return (maxlayer + 1);
+}
+
+/*
+ *---------------------------------------------------------------
+ * Find the maximum routing layer number defined by the LEF file
+ * Note that as defined, this returns value (layer_index + 1).
+ *---------------------------------------------------------------
+ */
+
+int
+LefGetMaxRouteLayer(void)
+{
+ int maxlayer = -1;
+ LefList lefl;
+
+ for (lefl = LefInfo; lefl; lefl = lefl->next) {
+ if (lefl->lefClass != CLASS_ROUTE) continue;
+ if (lefl->type > maxlayer)
+ maxlayer = lefl->type;
+ }
+ return (maxlayer + 1);
+}
+
+/*
+ *------------------------------------------------------------
+ * Return the route keepout area, defined as the route space
+ * plus 1/2 the route width. This is the distance outward
+ * from an obstruction edge within which one cannot place a
+ * route.
+ *
+ * If no route layer is defined, then we pick up the value
+ * from information in the route.cfg file (if any). Here
+ * we define it as the route pitch less 1/2 the route width,
+ * which is the same as above if the route pitch has been
+ * chosen for minimum spacing.
+ *
+ * If all else fails, return zero.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteKeepout(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.width / 2.0
+ + lefl->info.route.spacing->spacing;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Similar routine to the above. Return the route width for
+ * a route layer. Return value in microns. If there is no
+ * LEF file information about the route width, then return
+ * half of the minimum route pitch.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteWidth(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.width;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Similar to the above, return the width of a via. Arguments
+ * are the via record, the layer to check the width of, and
+ * direction "dir" = 1 for height and 0 for width.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetViaWidth(LefList lefl, int layer, int dir)
+{
+ double width, maxwidth;
+ DSEG lrect;
+
+ maxwidth = 0.0;
+
+ if (lefl->lefClass == CLASS_VIA) {
+ if (lefl->info.via.area.layer == layer) {
+ if (dir)
+ width = lefl->info.via.area.y2 - lefl->info.via.area.y1;
+ else
+ width = lefl->info.via.area.x2 - lefl->info.via.area.x1;
+ if (width > maxwidth) maxwidth = width;
+ }
+ for (lrect = lefl->info.via.lr; lrect; lrect = lrect->next) {
+ if (lrect->layer == layer) {
+ if (dir)
+ width = lrect->y2 - lrect->y1;
+ else
+ width = lrect->x2 - lrect->x1;
+ if (width > maxwidth) maxwidth = width;
+ }
+ }
+ }
+ return maxwidth / 2;
+}
+
+/*
+ *------------------------------------------------------------
+ * Similar routine to the above. Return the route offset for
+ * a route layer. Return value in microns. If there is no
+ * LEF file information about the route offset, then return
+ * half of the minimum route pitch.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteOffset(int layer)
+{
+ LefList lefl;
+ u_char o;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ o = lefl->info.route.hdirection;
+ if (o == TRUE)
+ return lefl->info.route.offsety;
+ else
+ return lefl->info.route.offsetx;
+ }
+ }
+ return 0.0;
+}
+
+double
+LefGetRouteOffsetX(int layer)
+{
+ LefList lefl;
+ u_char o;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.offsetx;
+ }
+ }
+ return 0.0;
+}
+
+double
+LefGetRouteOffsetY(int layer)
+{
+ LefList lefl;
+ u_char o;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.offsety;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Find and return the minimum metal area requirement for a
+ * route layer.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteMinArea(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.minarea;
+ }
+ }
+ return 0.0; /* Assume no minimum area requirement */
+}
+
+/*
+ *------------------------------------------------------------
+ * Get route spacing rule (minimum width)
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteSpacing(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ if (lefl->info.route.spacing)
+ return lefl->info.route.spacing->spacing;
+ else
+ return 0.0;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Find route spacing to a metal layer of specific width
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteWideSpacing(int layer, double width)
+{
+ LefList lefl;
+ lefSpacingRule *srule;
+ double spacing;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ // Prepare a default in case of bad values
+ spacing = lefl->info.route.spacing->spacing;
+ for (srule = lefl->info.route.spacing; srule; srule = srule->next) {
+ if (srule->width > width) break;
+ spacing = srule->spacing;
+ }
+ return spacing;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *-----------------------------------------------------------------
+ * Get the route pitch in the preferred direction for a given layer
+ *-----------------------------------------------------------------
+ */
+
+double
+LefGetRoutePitch(int layer)
+{
+ LefList lefl;
+ u_char o;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ o = lefl->info.route.hdirection;
+ if (o == TRUE)
+ return lefl->info.route.pitchy;
+ else
+ return lefl->info.route.pitchx;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route pitch in X for a given layer
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRoutePitchX(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.pitchx;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route pitch in Y for a given layer
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRoutePitchY(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.pitchy;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Set the route pitch in X for a given layer
+ *------------------------------------------------------------
+ */
+
+void
+LefSetRoutePitchX(int layer, double value)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ lefl->info.route.pitchx = value;
+ }
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ * Set the route pitch in Y for a given layer
+ *------------------------------------------------------------
+ */
+
+void
+LefSetRoutePitchY(int layer, double value)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ lefl->info.route.pitchy = value;
+ }
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route name for a given layer
+ *------------------------------------------------------------
+ */
+
+char *
+LefGetRouteName(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->lefName;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route orientation for the given layer,
+ * where the result is 1 for horizontal, 0 for vertical, and
+ * -1 if the layer is not found.
+ *------------------------------------------------------------
+ */
+
+int
+LefGetRouteOrientation(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return (int)lefl->info.route.hdirection;
+ }
+ }
+ return -1;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route resistance and capacitance information.
+ * Fill in the pointer values with the relevant information.
+ * Return 0 on success, -1 if the layer is not found.
+ *------------------------------------------------------------
+ */
+
+int
+LefGetRouteRCvalues(int layer, double *areacap, double *edgecap,
+ double *respersq)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ *areacap = (double)lefl->info.route.areacap;
+ *edgecap = (double)lefl->info.route.edgecap;
+ *respersq = (double)lefl->info.route.respersq;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the antenna violation area ratio for the given layer.
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteAreaRatio(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.antenna;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the antenna violation area calculation method for the
+ * given layer.
+ *------------------------------------------------------------
+ */
+
+u_char
+LefGetRouteAntennaMethod(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.method;
+ }
+ }
+ return CALC_NONE;
+}
+
+/*
+ *------------------------------------------------------------
+ * Get the route metal layer thickness (if any is defined)
+ *------------------------------------------------------------
+ */
+
+double
+LefGetRouteThickness(int layer)
+{
+ LefList lefl;
+
+ lefl = LefFindLayerByNum(layer);
+ if (lefl) {
+ if (lefl->lefClass == CLASS_ROUTE) {
+ return lefl->info.route.thick;
+ }
+ }
+ return 0.0;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadLayers --
+ *
+ * Read a LEF "LAYER" record from the file.
+ * If "obstruct" is TRUE, returns the layer mapping
+ * for obstruction geometry as defined in the
+ * technology file (if it exists), and up to two
+ * types are returned (the second in the 3rd argument
+ * pointer).
+ *
+ * Results:
+ * Returns layer number or -1 if no matching type is found.
+ *
+ * Side Effects:
+ * Reads input from file f;
+ *
+ *------------------------------------------------------------
+ */
+
+int
+LefReadLayers(f, obstruct, lreturn)
+ FILE *f;
+ u_char obstruct;
+ int *lreturn;
+{
+ char *token;
+ int curlayer = -1;
+ LefList lefl = NULL;
+
+ token = LefNextToken(f, TRUE);
+ if (*token == ';')
+ {
+ LefError(LEF_ERROR, "Bad Layer statement\n");
+ return -1;
+ }
+ else
+ {
+ lefl = LefFindLayer(token);
+ if (lefl)
+ {
+ if (obstruct)
+ {
+ /* Use the obstruction type, if it is defined */
+ curlayer = lefl->obsType;
+ if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE))
+ curlayer = lefl->type;
+ else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT)
+ if (lreturn) *lreturn = lefl->info.via.obsType;
+ }
+ else
+ {
+ if (lefl->lefClass != CLASS_IGNORE)
+ curlayer = lefl->type;
+ }
+ }
+ if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE)))
+ {
+ /* CLASS_VIA in lefl record is a cut, and the layer */
+ /* geometry is ignored for the purpose of routing. */
+
+ if (lefl && (lefl->lefClass == CLASS_CUT)) {
+ int cuttype;
+
+ /* By the time a cut layer is being requested, */
+ /* presumably from a VIA definition, the route */
+ /* layers should all be defined, so start */
+ /* assigning layers to cuts. */
+
+ /* If a cut layer is found with an unassigned number, */
+ /* then assign it here. */
+ cuttype = LefGetMaxLayer();
+ if (cuttype < MAX_TYPES) {
+ lefl->type = cuttype;
+ curlayer = cuttype;
+ }
+ else
+ LefError(LEF_WARNING, "Too many cut types; type \"%s\" ignored.\n",
+ token);
+ }
+ else if ((!lefl) || (lefl->lefClass != CLASS_VIA))
+ LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token);
+ }
+ }
+ return curlayer;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadLayer --
+ *
+ * Read a LEF "LAYER" record from the file.
+ * If "obstruct" is TRUE, returns the layer mapping
+ * for obstruction geometry as defined in the
+ * technology file (if it exists).
+ *
+ * Results:
+ * Returns a layer number or -1 if no match is found.
+ *
+ * Side Effects:
+ * Reads input from file f;
+ *
+ *------------------------------------------------------------
+ */
+
+int
+LefReadLayer(FILE *f, u_char obstruct)
+{
+ return LefReadLayers(f, obstruct, (int *)NULL);
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadRect --
+ *
+ * Read a LEF "RECT" record from the file, and
+ * return a Rect in micron coordinates.
+ *
+ * Results:
+ * Returns a pointer to a Rect containing the micron
+ * coordinates, or NULL if an error occurred.
+ *
+ * Side Effects:
+ * Reads input from file f;
+ *
+ * Note:
+ * LEF/DEF does NOT define a RECT record as having (...)
+ * pairs, only routes. However, at least one DEF file
+ * contains this syntax, so it is checked.
+ *
+ *------------------------------------------------------------
+ */
+
+DSEG
+LefReadRect(FILE *f, int curlayer, float oscale)
+{
+ char *token;
+ float llx, lly, urx, ury;
+ static struct dseg_ paintrect;
+ u_char needMatch = FALSE;
+
+ token = LefNextToken(f, TRUE);
+ if (*token == '(')
+ {
+ token = LefNextToken(f, TRUE);
+ needMatch = TRUE;
+ }
+ if (!token || sscanf(token, "%f", &llx) != 1) goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &lly) != 1) goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (needMatch)
+ {
+ if (*token != ')') goto parse_error;
+ else token = LefNextToken(f, TRUE);
+ needMatch = FALSE;
+ }
+ if (*token == '(')
+ {
+ token = LefNextToken(f, TRUE);
+ needMatch = TRUE;
+ }
+ if (!token || sscanf(token, "%f", &urx) != 1) goto parse_error;
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &ury) != 1) goto parse_error;
+ if (needMatch)
+ {
+ token = LefNextToken(f, TRUE);
+ if (*token != ')') goto parse_error;
+ }
+ if (curlayer < 0) {
+ /* Issue warning but keep geometry with negative layer number */
+ LefError(LEF_WARNING, "No layer defined for RECT.\n");
+ }
+
+ /* Scale coordinates (microns to centimicrons) */
+
+ paintrect.x1 = llx / oscale;
+ paintrect.y1 = lly / oscale;
+ paintrect.x2 = urx / oscale;
+ paintrect.y2 = ury / oscale;
+ paintrect.layer = curlayer;
+ return (&paintrect);
+
+parse_error:
+ LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n");
+ return (DSEG)NULL;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadEnclosure --
+ *
+ * Read a LEF "ENCLOSURE" record from the file, and
+ * return a Rect in micron coordinates, representing
+ * the bounding box of the stated enclosure dimensions
+ * in both directions, centered on the origin.
+ *
+ * Results:
+ * Returns a pointer to a Rect containing the micron
+ * coordinates, or NULL if an error occurred.
+ *
+ * Side Effects:
+ * Reads input from file f
+ *
+ *------------------------------------------------------------
+ */
+
+DSEG
+LefReadEnclosure(FILE *f, int curlayer, float oscale)
+{
+ char *token;
+ float x, y, scale;
+ static struct dseg_ paintrect;
+
+ token = LefNextToken(f, TRUE);
+ if (!token) goto enc_parse_error;
+
+ if (sscanf(token, "%f", &x) != 1) {
+ if (!strcmp(token, "BELOW") || !strcmp(token, "ABOVE"))
+ /* NOTE: This creates two records but fails to differentiate */
+ /* between the layers, both of which will be -1 if this is a */
+ /* cut layer. Needs to be handled properly. */
+
+ token = LefNextToken(f, TRUE);
+ else
+ goto enc_parse_error;
+ }
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &y) != 1) goto enc_parse_error;
+
+ if (curlayer < 0) {
+ /* Issue warning but keep geometry with negative layer number */
+ LefError(LEF_ERROR, "No layer defined for RECT.\n");
+ }
+
+ /* Scale coordinates (microns to centimicrons) (doubled) */
+
+ scale = oscale / 2.0;
+
+ paintrect.x1 = -x / scale;
+ paintrect.y1 = -y / scale;
+ paintrect.x2 = x / scale;
+ paintrect.y2 = y / scale;
+ paintrect.layer = curlayer;
+ return (&paintrect);
+
+enc_parse_error:
+ LefError(LEF_ERROR, "Bad enclosure geometry: ENCLOSURE requires 2 values.\n");
+ return (DSEG)NULL;
+}
+
+/*
+ *------------------------------------------------------------
+ * Support routines for polygon reading
+ *------------------------------------------------------------
+ */
+
+#define HEDGE 0 /* Horizontal edge */
+#define REDGE 1 /* Rising edge */
+#define FEDGE -1 /* Falling edge */
+
+/*
+ *------------------------------------------------------------
+ * lefLowX ---
+ *
+ * Sort routine to find the lowest X coordinate between
+ * two DPOINT structures passed from qsort()
+ *------------------------------------------------------------
+ */
+
+int
+lefLowX(DPOINT *a, DPOINT *b)
+{
+ DPOINT p = *a;
+ DPOINT q = *b;
+
+ if (p->x < q->x)
+ return (-1);
+ if (p->x > q->x)
+ return (1);
+ return (0);
+}
+
+/*
+ *------------------------------------------------------------
+ * lefLowY ---
+ *
+ * Sort routine to find the lowest Y coordinate between
+ * two DPOINT structures passed from qsort()
+ *------------------------------------------------------------
+ */
+
+int
+lefLowY(DPOINT *a, DPOINT *b)
+{
+ DPOINT p = *a;
+ DPOINT q = *b;
+
+ if (p->y < q->y)
+ return (-1);
+ if (p->y > q->y)
+ return (1);
+ return (0);
+}
+
+/*
+ *------------------------------------------------------------
+ * lefOrient ---
+ *
+ * Assign a direction to each of the edges in a polygon.
+ *
+ * Note that edges have been sorted, but retain the original
+ * linked list pointers, from which we can determine the
+ * path orientation
+ *
+ *------------------------------------------------------------
+ */
+
+char
+lefOrient(DPOINT *edges, int nedges, int *dir)
+{
+ int n;
+ DPOINT p, q;
+
+ for (n = 0; n < nedges; n++)
+ {
+ p = edges[n];
+ q = edges[n]->next;
+
+ if (p->y == q->y)
+ {
+ dir[n] = HEDGE;
+ continue;
+ }
+ if (p->x == q->x)
+ {
+ if (p->y < q->y)
+ {
+ dir[n] = REDGE;
+ continue;
+ }
+ if (p->y > q->y)
+ {
+ dir[n] = FEDGE;
+ continue;
+ }
+ /* Point connects to itself */
+ dir[n] = HEDGE;
+ continue;
+ }
+ /* It's not Manhattan, folks. */
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ *------------------------------------------------------------
+ * lefCross ---
+ *
+ * See if an edge crosses a particular area.
+ * Return TRUE if edge if vertical and if it crosses the
+ * y-range defined by ybot and ytop. Otherwise return
+ * FALSE.
+ *------------------------------------------------------------
+ */
+
+char
+lefCross(DPOINT edge, int dir, double ybot, double ytop)
+{
+ double ebot, etop;
+
+ switch (dir)
+ {
+ case REDGE:
+ ebot = edge->y;
+ etop = edge->next->y;
+ return (ebot <= ybot && etop >= ytop);
+
+ case FEDGE:
+ ebot = edge->next->y;
+ etop = edge->y;
+ return (ebot <= ybot && etop >= ytop);
+ }
+ return (FALSE);
+}
+
+
+/*
+ *------------------------------------------------------------
+ * LefPolygonToRects --
+ *
+ * Convert Geometry information from a POLYGON statement
+ * into rectangles. NOTE: For now, this routine
+ * assumes that all points are Manhattan. It will flag
+ * non-Manhattan geometry
+ *
+ * the DSEG pointed to by rectListPtr is updated by
+ * having the list of rectangles appended to it.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefPolygonToRects(DSEG *rectListPtr, DPOINT pointlist)
+{
+ DPOINT ptail, p, *pts, *edges;
+ DSEG rtail, rex, new;
+ int npts = 0;
+ int *dir;
+ int curr, wrapno, n;
+ double xbot, xtop, ybot, ytop;
+
+ if (pointlist == NULL) return;
+
+ /* Close the path by duplicating 1st point if necessary */
+
+ for (ptail = pointlist; ptail->next; ptail = ptail->next);
+
+ if ((ptail->x != pointlist->x) || (ptail->y != pointlist->y))
+ {
+ p = (DPOINT)malloc(sizeof(struct dpoint_));
+ p->x = pointlist->x;
+ p->y = pointlist->y;
+ p->layer = pointlist->layer;
+ p->next = NULL;
+ ptail->next = p;
+ }
+
+ // To do: Break out non-manhattan parts here.
+ // See CIFMakeManhattanPath in magic-8.0
+
+ rex = NULL;
+ for (p = pointlist; p->next; p = p->next, npts++);
+ pts = (DPOINT *)malloc(npts * sizeof(DPOINT));
+ edges = (DPOINT *)malloc(npts * sizeof(DPOINT));
+ dir = (int *)malloc(npts * sizeof(int));
+ npts = 0;
+
+ for (p = pointlist; p->next; p = p->next, npts++)
+ {
+ // pts and edges are two lists of pointlist entries
+ // that are NOT linked lists and can be shuffled
+ // around by qsort(). The linked list "next" pointers
+ // *must* be retained.
+
+ pts[npts] = p;
+ edges[npts] = p;
+ }
+
+ if (npts < 4)
+ {
+ LefError(LEF_ERROR, "Polygon with fewer than 4 points.\n");
+ goto done;
+ }
+
+ /* Sort points by low y, edges by low x */
+ qsort((char *)pts, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowY);
+ qsort((char *)edges, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowX);
+
+ /* Find out which direction each edge points */
+
+ if (!lefOrient(edges, npts, dir))
+ {
+ LefError(LEF_ERROR, "I can't handle non-manhattan polygons!\n");
+ goto done;
+ }
+
+ /* Scan the polygon from bottom to top. At each step, process
+ * a minimum-sized y-range of the polygon (i.e., a range such that
+ * there are no vertices inside the range). Use wrap numbers
+ * based on the edge orientations to determine how much of the
+ * x-range for this y-range should contain material.
+ */
+
+ for (curr = 1; curr < npts; curr++)
+ {
+ /* Find the next minimum-sized y-range. */
+
+ ybot = pts[curr - 1]->y;
+ while (ybot == pts[curr]->y)
+ if (++curr >= npts) goto done;
+ ytop = pts[curr]->y;
+
+ /* Process all the edges that cross the y-range, from left
+ * to right.
+ */
+
+ for (wrapno = 0, n = 0; n < npts; n++)
+ {
+ if (wrapno == 0) xbot = edges[n]->x;
+ if (!lefCross(edges[n], dir[n], ybot, ytop))
+ continue;
+ wrapno += (dir[n] == REDGE) ? 1 : -1;
+ if (wrapno == 0)
+ {
+ xtop = edges[n]->x;
+ if (xbot == xtop) continue;
+ new = (DSEG)malloc(sizeof(struct dseg_));
+ new->x1 = xbot;
+ new->x2 = xtop;
+ new->y1 = ybot;
+ new->y2 = ytop;
+ new->layer = edges[n]->layer;
+ new->next = rex;
+ rex = new;
+ }
+ }
+ }
+
+done:
+ free(edges);
+ free(dir);
+ free(pts);
+
+ if (*rectListPtr == NULL)
+ *rectListPtr = rex;
+ else
+ {
+ for (rtail = *rectListPtr; rtail->next; rtail = rtail->next);
+ rtail->next = rex;
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadPolygon --
+ *
+ * Read Geometry information from a POLYGON statement
+ *
+ *------------------------------------------------------------
+ */
+
+DPOINT
+LefReadPolygon(FILE *f, int curlayer, float oscale)
+{
+ DPOINT plist = NULL, newPoint;
+ char *token;
+ double px, py;
+
+ while (1)
+ {
+ token = LefNextToken(f, TRUE);
+ if (token == NULL || *token == ';') break;
+ if (sscanf(token, "%lg", &px) != 1)
+ {
+ LefError(LEF_ERROR, "Bad X value in polygon.\n");
+ LefEndStatement(f);
+ break;
+ }
+
+ token = LefNextToken(f, TRUE);
+ if (token == NULL || *token == ';')
+ {
+ LefError(LEF_ERROR, "Missing Y value in polygon point!\n");
+ break;
+ }
+ if (sscanf(token, "%lg", &py) != 1)
+ {
+ LefError(LEF_ERROR, "Bad Y value in polygon.\n");
+ LefEndStatement(f);
+ break;
+ }
+
+ newPoint = (DPOINT)malloc(sizeof(struct dpoint_));
+ newPoint->x = px / (double)oscale;
+ newPoint->y = py / (double)oscale;
+ newPoint->layer = curlayer;
+ newPoint->next = plist;
+ plist = newPoint;
+ }
+
+ return plist;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadGeometry --
+ *
+ * Read Geometry information from a LEF file.
+ * Used for PORT records and OBS statements.
+ *
+ * Results:
+ * Returns a linked list of all areas and types
+ * painted.
+ *
+ * Side Effects:
+ * Reads input from file f;
+ * Paints into the GATE lefMacro.
+ *
+ *------------------------------------------------------------
+ */
+
+enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH,
+ LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS,
+ LEF_GEOMETRY_END};
+
+DSEG
+LefReadGeometry(GATE lefMacro, FILE *f, float oscale)
+{
+ int curlayer = -1, otherlayer = -1;
+
+ char *token;
+ int keyword;
+ DSEG rectList = (DSEG)NULL;
+ DSEG paintrect, newRect;
+ DPOINT pointlist;
+
+ static char *geometry_keys[] = {
+ "LAYER",
+ "WIDTH",
+ "PATH",
+ "RECT",
+ "POLYGON",
+ "VIA",
+ "CLASS",
+ "END",
+ NULL
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, geometry_keys);
+ if (keyword < 0)
+ {
+ LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
+ token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case LEF_LAYER:
+ curlayer = LefReadLayers(f, FALSE, &otherlayer);
+ LefEndStatement(f);
+ break;
+ case LEF_WIDTH:
+ LefEndStatement(f);
+ break;
+ case LEF_PATH:
+ LefEndStatement(f);
+ break;
+ case LEF_RECT:
+ paintrect = (curlayer < 0) ? NULL : LefReadRect(f, curlayer, oscale);
+ if (paintrect)
+ {
+ /* Remember the area and layer */
+ newRect = (DSEG)malloc(sizeof(struct dseg_));
+ *newRect = *paintrect;
+ newRect->next = rectList;
+ rectList = newRect;
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_POLYGON:
+ pointlist = LefReadPolygon(f, curlayer, oscale);
+ LefPolygonToRects(&rectList, pointlist);
+ break;
+ case LEF_VIA:
+ LefEndStatement(f);
+ break;
+ case LEF_PORT_CLASS:
+ LefEndStatement(f);
+ break;
+ case LEF_GEOMETRY_END:
+ if (!LefParseEndStatement(f, NULL))
+ {
+ LefError(LEF_ERROR, "Geometry (PORT or OBS) END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == LEF_GEOMETRY_END) break;
+ }
+ return rectList;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadPort --
+ *
+ * A wrapper for LefReadGeometry, which adds a label
+ * to the last rectangle generated by the geometry
+ * parsing.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Reads input from file f;
+ * Paints into the GATE lefMacro.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, pinArea, oscale)
+ GATE lefMacro;
+ FILE *f;
+ char *pinName;
+ int pinNum, pinDir, pinUse;
+ double pinArea;
+ float oscale;
+{
+ DSEG rectList, rlist;
+ BUS bus;
+
+ rectList = LefReadGeometry(lefMacro, f, oscale);
+
+ if (pinNum >= 0) {
+ int nodealloc, orignodes, ival;
+ char *aptr;
+
+ if (lefMacro->nodes <= pinNum) {
+ orignodes = lefMacro->nodes;
+ lefMacro->nodes = (pinNum + 1);
+ nodealloc = lefMacro->nodes / 10;
+ if (nodealloc > (orignodes / 10)) {
+ nodealloc++;
+ lefMacro->taps = (DSEG *)realloc(lefMacro->taps,
+ nodealloc * 10 * sizeof(DSEG));
+ lefMacro->noderec = (NODE *)realloc(lefMacro->noderec,
+ nodealloc * 10 * sizeof(NODE));
+ lefMacro->direction = (u_char *)realloc(lefMacro->direction,
+ nodealloc * 10 * sizeof(u_char));
+ lefMacro->area = (float *)realloc(lefMacro->area,
+ nodealloc * 10 * sizeof(float));
+ lefMacro->use = (u_char *)realloc(lefMacro->use,
+ nodealloc * 10 * sizeof(u_char));
+ lefMacro->netnum = (int *)realloc(lefMacro->netnum,
+ nodealloc * 10 * sizeof(int));
+ lefMacro->node = (char **)realloc(lefMacro->node,
+ nodealloc * 10 * sizeof(char *));
+ }
+ }
+ lefMacro->taps[pinNum] = rectList;
+ lefMacro->noderec[pinNum] = NULL;
+ lefMacro->area[pinNum] = 0.0;
+ lefMacro->direction[pinNum] = (u_char)pinDir;
+ lefMacro->area[pinNum] = pinArea;
+ lefMacro->use[pinNum] = (u_char)pinUse;
+ lefMacro->netnum[pinNum] = -1;
+ if (pinName != NULL) {
+ lefMacro->node[pinNum] = strdup(pinName);
+
+ /* Check for bus delimiters */
+ aptr = strrchr(pinName, delimiter);
+ if (aptr != NULL) {
+ if (sscanf(aptr + 1, "%d", &ival) == 1) {
+ *aptr = '\0';
+
+ /* Add to bus list if needed, or adjust bounds */
+ for (bus = lefMacro->bus; bus; bus = bus->next) {
+ if (!strcmp(pinName, bus->busname)) {
+ if (ival > bus->high) bus->high = ival;
+ if (ival < bus->low) bus->low = ival;
+ break;
+ }
+ }
+ if (bus == NULL) {
+ bus = (BUS)malloc(sizeof(struct bus_));
+ bus->busname = strdup(pinName);
+ bus->high = bus->low = ival;
+ bus->next = lefMacro->bus;
+ lefMacro->bus = bus;
+ }
+ *aptr = '[';
+ }
+ }
+ }
+ else
+ lefMacro->node[pinNum] = NULL;
+ }
+ else {
+ while (rectList) {
+ rlist = rectList->next;
+ free(rectList);
+ rectList = rlist;
+ }
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ * LefReadPin --
+ *
+ * Read a PIN statement from a LEF file.
+ *
+ * Results:
+ * 0 if the pin had a port (success), 1 if not (indicating
+ * an unused pin that should be ignored).
+ *
+ * Side Effects:
+ * Reads input from file f;
+ * Paints into the GATE lefMacro.
+ *
+ *------------------------------------------------------------
+ */
+
+enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE,
+ LEF_ANTENNADIFF, LEF_ANTENNAGATE, LEF_ANTENNAMOD,
+ LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE,
+ LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END};
+
+int
+LefReadPin(lefMacro, f, pinname, pinNum, oscale)
+ GATE lefMacro;
+ FILE *f;
+ char *pinname;
+ int pinNum;
+ float oscale;
+{
+ char *token;
+ int keyword, subkey;
+ int pinDir = PORT_CLASS_DEFAULT;
+ int pinUse = PORT_USE_DEFAULT;
+ float pinArea = 0.0;
+ int retval = 1;
+
+ static char *pin_keys[] = {
+ "DIRECTION",
+ "USE",
+ "PORT",
+ "CAPACITANCE",
+ "ANTENNADIFFAREA",
+ "ANTENNAGATEAREA",
+ "ANTENNAMODEL",
+ "ANTENNAPARTIALMETALAREA",
+ "ANTENNAPARTIALMETALSIDEAREA",
+ "ANTENNAMAXAREACAR",
+ "ANTENNAMAXSIDEAREACAR",
+ "SHAPE",
+ "NETEXPR",
+ "END",
+ NULL
+ };
+
+ static char *pin_classes[] = {
+ "DEFAULT",
+ "INPUT",
+ "OUTPUT",
+ "OUTPUT TRISTATE",
+ "INOUT",
+ "FEEDTHRU",
+ NULL
+ };
+
+ static int lef_class_to_bitmask[] = {
+ PORT_CLASS_DEFAULT,
+ PORT_CLASS_INPUT,
+ PORT_CLASS_OUTPUT,
+ PORT_CLASS_TRISTATE,
+ PORT_CLASS_BIDIRECTIONAL,
+ PORT_CLASS_FEEDTHROUGH
+ };
+
+ static char *pin_uses[] = {
+ "DEFAULT",
+ "SIGNAL",
+ "ANALOG",
+ "POWER",
+ "GROUND",
+ "CLOCK",
+ "TIEOFF",
+ "ANALOG",
+ "SCAN",
+ "RESET",
+ NULL
+ };
+
+ static int lef_use_to_bitmask[] = {
+ PORT_USE_DEFAULT,
+ PORT_USE_SIGNAL,
+ PORT_USE_ANALOG,
+ PORT_USE_POWER,
+ PORT_USE_GROUND,
+ PORT_USE_CLOCK
+ };
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, pin_keys);
+ if (keyword < 0)
+ {
+ LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
+ token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case LEF_DIRECTION:
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, pin_classes);
+ if (subkey < 0)
+ LefError(LEF_ERROR, "Improper DIRECTION statement\n");
+ else
+ pinDir = lef_class_to_bitmask[subkey];
+ LefEndStatement(f);
+ break;
+ case LEF_USE:
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, pin_uses);
+ if (subkey < 0)
+ LefError(LEF_ERROR, "Improper USE statement\n");
+ else
+ pinUse = lef_use_to_bitmask[subkey];
+ LefEndStatement(f);
+ break;
+ case LEF_PORT:
+ LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, pinArea, oscale);
+ retval = 0;
+ break;
+ case LEF_ANTENNAGATE:
+ /* Read off the next value as the pin's antenna gate area. */
+ /* The layers or layers are not recorded. */
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%g", &pinArea);
+ LefEndStatement(f);
+ break;
+ case LEF_CAPACITANCE:
+ case LEF_ANTENNADIFF:
+ case LEF_ANTENNAMOD:
+ case LEF_ANTENNAPAR:
+ case LEF_ANTENNAPARSIDE:
+ case LEF_ANTENNAMAX:
+ case LEF_ANTENNAMAXSIDE:
+ case LEF_NETEXPR:
+ case LEF_SHAPE:
+ LefEndStatement(f); /* Ignore. . . */
+ break;
+ case LEF_PIN_END:
+ if (!LefParseEndStatement(f, pinname))
+ {
+ LefError(LEF_ERROR, "Pin END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == LEF_PIN_END) break;
+ }
+ return retval;
+}
+
+/*
+ *------------------------------------------------------------
+ * LefEndStatement --
+ *
+ * Read file input to EOF or a ';' token (end-of-statement)
+ * If we encounter a quote, make sure we don't terminate
+ * the statement on a semicolon that is part of the
+ * quoted material.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefEndStatement(FILE *f)
+{
+ char *token;
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ if (*token == ';') break;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefReadMacro --
+ *
+ * Read in a MACRO section from a LEF file.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Creates a new cell definition in the database.
+ *
+ *------------------------------------------------------------
+ */
+
+enum lef_macro_keys {LEF_CLASS = 0, LEF_SIZE, LEF_ORIGIN,
+ LEF_SYMMETRY, LEF_SOURCE, LEF_SITE, LEF_PIN, LEF_OBS,
+ LEF_TIMING, LEF_FOREIGN, LEF_MACRO_END};
+
+void
+LefReadMacro(f, mname, oscale)
+ FILE *f; /* LEF file being read */
+ char *mname; /* name of the macro */
+ float oscale; /* scale factor to um, usually 1 */
+{
+ GATE lefMacro, altMacro;
+ char *token, *pname, tsave[128];
+ int keyword, pinNum, subkey;
+ float x, y;
+ u_char has_size, is_imported = FALSE;
+ struct dseg_ lefBBox;
+
+ static char *macro_keys[] = {
+ "CLASS",
+ "SIZE",
+ "ORIGIN",
+ "SYMMETRY",
+ "SOURCE",
+ "SITE",
+ "PIN",
+ "OBS",
+ "TIMING",
+ "FOREIGN",
+ "END",
+ NULL
+ };
+
+ static char *macro_classes[] = {
+ "DEFAULT",
+ "CORE",
+ "BLOCK",
+ "PAD",
+ "RING",
+ "COVER",
+ "ENDCAP",
+ NULL
+ };
+
+ static int lef_macro_class_to_bitmask[] = {
+ MACRO_CLASS_DEFAULT,
+ MACRO_CLASS_CORE,
+ MACRO_CLASS_BLOCK,
+ MACRO_CLASS_PAD,
+ MACRO_CLASS_RING,
+ MACRO_CLASS_COVER,
+ MACRO_CLASS_ENDCAP
+ };
+
+ static char *macro_subclasses[] = {
+ ";",
+ "SPACER",
+ "ANTENNACELL",
+ "WELLTAP",
+ "TIEHIGH",
+ "TIELOW",
+ "FEEDTHRU"
+ };
+
+ static int lef_macro_subclass_to_bitmask[] = {
+ MACRO_SUBCLASS_NONE,
+ MACRO_SUBCLASS_SPACER,
+ MACRO_SUBCLASS_ANTENNA,
+ MACRO_SUBCLASS_WELLTAP,
+ MACRO_SUBCLASS_TIEHIGH,
+ MACRO_SUBCLASS_TIELOW,
+ MACRO_SUBCLASS_FEEDTHRU
+ };
+
+ /* Start by creating a new celldef */
+
+ lefMacro = (GATE)NULL;
+ for (altMacro = GateInfo; altMacro; altMacro = altMacro->next)
+ {
+ if (!strcmp(altMacro->gatename, mname)) {
+ lefMacro = altMacro;
+ break;
+ }
+ }
+
+ while (lefMacro)
+ {
+ int suffix;
+ char newname[256];
+
+ altMacro = lefMacro;
+ for (suffix = 1; altMacro != NULL; suffix++)
+ {
+ sprintf(newname, "%250s_%d", mname, suffix);
+ for (altMacro = GateInfo; altMacro; altMacro = altMacro->next)
+ if (!strcmp(altMacro->gatename, newname))
+ break;
+ }
+ LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. "
+ "Renaming original cell \"%s\"\n", mname, newname);
+
+ lefMacro->gatename = strdup(newname);
+ lefMacro = lefFindCell(mname);
+ }
+
+ // Create the new cell
+ lefMacro = (GATE)malloc(sizeof(struct gate_));
+ lefMacro->gatename = strdup(mname);
+ lefMacro->gatetype = NULL;
+ lefMacro->gateclass = MACRO_CLASS_DEFAULT;
+ lefMacro->gatesubclass = MACRO_SUBCLASS_NONE;
+ lefMacro->width = 0.0;
+ lefMacro->height = 0.0;
+ lefMacro->placedX = 0.0;
+ lefMacro->placedY = 0.0;
+ lefMacro->obs = (DSEG)NULL;
+ lefMacro->next = GateInfo;
+ lefMacro->last = (GATE)NULL;
+ lefMacro->nodes = 0;
+ lefMacro->orient = 0;
+ // Allocate memory for up to 10 pins initially
+ lefMacro->taps = (DSEG *)malloc(10 * sizeof(DSEG));
+ lefMacro->noderec = (NODE *)malloc(10 * sizeof(NODE));
+ lefMacro->direction = (u_char *)malloc(10 * sizeof(u_char));
+ lefMacro->area = (float *)malloc(10 * sizeof(float));
+ lefMacro->use = (u_char *)malloc(10 * sizeof(u_char));
+ lefMacro->netnum = (int *)malloc(10 * sizeof(int));
+ lefMacro->node = (char **)malloc(10 * sizeof(char *));
+ // Fill in 1st entry
+ lefMacro->taps[0] = NULL;
+ lefMacro->noderec[0] = NULL;
+ lefMacro->area[0] = 0.0;
+ lefMacro->node[0] = NULL;
+ lefMacro->bus = NULL;
+ lefMacro->netnum[0] = -1;
+ lefMacro->clientdata = (void *)NULL;
+ GateInfo = lefMacro;
+
+ /* Set gate type to the site name for site definitions */
+ pname = mname;
+ if (!strncmp(mname, "site_", 5)) pname += 5;
+
+ /* Initial values */
+ pinNum = 0;
+ has_size = FALSE;
+ lefBBox.x2 = lefBBox.x1 = 0.0;
+ lefBBox.y2 = lefBBox.y1 = 0.0;
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, macro_keys);
+ if (keyword < 0)
+ {
+ LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
+ token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case LEF_CLASS:
+ token = LefNextToken(f, TRUE);
+ subkey = Lookup(token, macro_classes);
+ if (subkey < 0) {
+ LefError(LEF_ERROR, "Improper macro CLASS statement\n");
+ lefMacro->gateclass = MACRO_CLASS_DEFAULT;
+ }
+ else
+ lefMacro->gateclass = lef_macro_class_to_bitmask[subkey];
+ token = LefNextToken(f, TRUE);
+ if (token) {
+ subkey = Lookup(token, macro_subclasses);
+ if (subkey < 0) {
+ lefMacro->gatesubclass = MACRO_SUBCLASS_NONE;
+ LefEndStatement(f);
+ }
+ else if (subkey > 0) {
+ lefMacro->gatesubclass = lef_macro_subclass_to_bitmask[subkey];
+ LefEndStatement(f);
+ }
+ else
+ lefMacro->gatesubclass = MACRO_SUBCLASS_NONE;
+ }
+ else
+ LefEndStatement(f);
+ break;
+ case LEF_SIZE:
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &x) != 1) goto size_error;
+ token = LefNextToken(f, TRUE); /* skip keyword "BY" */
+ if (!token) goto size_error;
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &y) != 1) goto size_error;
+
+ lefBBox.x2 = x + lefBBox.x1;
+ lefBBox.y2 = y + lefBBox.y1;
+ has_size = TRUE;
+ LefEndStatement(f);
+ break;
+size_error:
+ LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n");
+ LefEndStatement(f);
+ break;
+ case LEF_ORIGIN:
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &x) != 1) goto origin_error;
+ token = LefNextToken(f, TRUE);
+ if (!token || sscanf(token, "%f", &y) != 1) goto origin_error;
+
+ lefBBox.x1 = -x;
+ lefBBox.y1 = -y;
+ if (has_size)
+ {
+ lefBBox.x2 += lefBBox.x1;
+ lefBBox.y2 += lefBBox.y1;
+ }
+ LefEndStatement(f);
+ break;
+origin_error:
+ LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n");
+ LefEndStatement(f);
+ break;
+ case LEF_SYMMETRY:
+ token = LefNextToken(f, TRUE);
+ if (*token != '\n')
+ // DBPropPut(lefMacro, "LEFsymmetry", token + strlen(token) + 1);
+ ;
+ LefEndStatement(f);
+ break;
+ case LEF_SOURCE:
+ token = LefNextToken(f, TRUE);
+ if (*token != '\n')
+ // DBPropPut(lefMacro, "LEFsource", token);
+ ;
+ LefEndStatement(f);
+ break;
+ case LEF_SITE:
+ token = LefNextToken(f, TRUE);
+ if (*token != '\n')
+ // DBPropPut(lefMacro, "LEFsite", token);
+ ;
+ LefEndStatement(f);
+ break;
+ case LEF_PIN:
+ token = LefNextToken(f, TRUE);
+ /* Diagnostic */
+ /*
+ fprintf(stdout, " Macro defines pin %s\n", token);
+ */
+ sprintf(tsave, "%.127s", token);
+ if (is_imported)
+ LefSkipSection(f, tsave);
+ else
+ if (LefReadPin(lefMacro, f, tsave, pinNum, oscale) == 0)
+ pinNum++;
+ break;
+ case LEF_OBS:
+ /* Diagnostic */
+ /*
+ fprintf(stdout, " Macro defines obstruction\n");
+ */
+ if (is_imported)
+ LefSkipSection(f, NULL);
+ else
+ lefMacro->obs = LefReadGeometry(lefMacro, f, oscale);
+ break;
+ case LEF_TIMING:
+ LefSkipSection(f, macro_keys[LEF_TIMING]);
+ break;
+ case LEF_FOREIGN:
+ LefEndStatement(f);
+ break;
+ case LEF_MACRO_END:
+ pname = mname;
+ if (!strncmp(mname, "site_", 5)) pname += 5;
+ if (!LefParseEndStatement(f, pname))
+ {
+ LefError(LEF_ERROR, "Macro END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == LEF_MACRO_END) break;
+ }
+
+ /* Finish up creating the cell */
+
+ if (lefMacro) {
+ if (has_size) {
+ lefMacro->width = (lefBBox.x2 - lefBBox.x1);
+ lefMacro->height = (lefBBox.y2 - lefBBox.y1);
+
+ /* "placed" for macros (not instances) corresponds to the */
+ /* cell origin. */
+
+ lefMacro->placedX = lefBBox.x1;
+ lefMacro->placedY = lefBBox.y1;
+ }
+ else {
+ LefError(LEF_ERROR, "Gate %s has no size information!\n", lefMacro->gatename);
+ }
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefAddViaGeometry --
+ *
+ * Read in geometry for a VIA section from a LEF or DEF
+ * file.
+ *
+ * f LEF file being read
+ * lefl pointer to via info
+ * curlayer current tile type
+ * oscale output scaling
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Adds to the lefLayer record for a via definition.
+ *
+ *------------------------------------------------------------
+ */
+
+void
+LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale)
+{
+ DSEG currect;
+ DSEG viarect;
+
+ /* Rectangles for vias are read in units of 1/2 lambda */
+ currect = LefReadRect(f, curlayer, (oscale / 2));
+ if (currect == NULL) return;
+
+ /* First rect goes into info.via.area, others go into info.via.lr */
+ if (lefl->info.via.area.layer < 0)
+ {
+ lefl->info.via.area = *currect;
+
+ /* If entries exist for info.via.lr, this is a via GENERATE */
+ /* statement, and metal enclosures have been parsed. Therefore */
+ /* add the via dimensions to the enclosure rectangles. */
+
+ viarect = lefl->info.via.lr;
+ while (viarect != NULL) {
+ viarect->x1 += currect->x1;
+ viarect->x2 += currect->x2;
+ viarect->y1 += currect->y1;
+ viarect->y2 += currect->y2;
+ viarect = viarect->next;
+ }
+ }
+ else
+ {
+ viarect = (DSEG)malloc(sizeof(struct dseg_));
+ *viarect = *currect;
+ viarect->next = lefl->info.via.lr;
+ lefl->info.via.lr = viarect;
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefNewRoute ---
+ *
+ * Allocate space for and fill out default records of a
+ * route layer.
+ *
+ *------------------------------------------------------------
+ */
+
+LefList LefNewRoute(char *name)
+{
+ LefList lefl;
+
+ lefl = (LefList)malloc(sizeof(lefLayer));
+ lefl->type = -1;
+ lefl->obsType = -1;
+ lefl->lefClass = CLASS_IGNORE; /* For starters */
+ lefl->lefName = strdup(name);
+
+ return lefl;
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefNewVia ---
+ *
+ * Allocate space for and fill out default records of a
+ * via definition.
+ *
+ *------------------------------------------------------------
+ */
+
+LefList LefNewVia(char *name)
+{
+ LefList lefl;
+
+ lefl = (LefList)calloc(1, sizeof(lefLayer));
+ lefl->type = -1;
+ lefl->obsType = -1;
+ lefl->lefClass = CLASS_VIA;
+ lefl->info.via.area.x1 = 0.0;
+ lefl->info.via.area.y1 = 0.0;
+ lefl->info.via.area.x2 = 0.0;
+ lefl->info.via.area.y2 = 0.0;
+ lefl->info.via.area.layer = -1;
+ lefl->info.via.cell = (GATE)NULL;
+ lefl->info.via.lr = (DSEG)NULL;
+ lefl->info.via.generated = FALSE;
+ lefl->info.via.respervia = 0.0;
+ lefl->info.via.spacing = (lefSpacingRule *)NULL;
+ if (name != NULL)
+ lefl->lefName = strdup(name);
+ else
+ lefl->lefName = NULL;
+
+ return lefl;
+}
+
+/* Note: Used directly below, as it is passed to variable "mode"
+ * in LefReadLayerSection(). However, mainly used in LefRead().
+ */
+
+enum lef_sections {LEF_VERSION = 0,
+ LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID,
+ LEF_USEMINSPACING, LEF_CLEARANCEMEASURE,
+ LEF_NAMESCASESENSITIVE, LEF_PROPERTYDEFS, LEF_UNITS,
+ LEF_SECTION_LAYER, LEF_SECTION_VIA, LEF_SECTION_VIARULE,
+ LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY,
+ LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP,
+ LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO,
+ LEF_END};
+/*
+ *------------------------------------------------------------
+ *
+ * LefReadLayerSection --
+ *
+ * Read in a LAYER, VIA, or VIARULE section from a LEF file.
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ *
+ *------------------------------------------------------------
+ */
+
+enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH,
+ LEF_LAYER_MINWIDTH, LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA,
+ LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE,
+ LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET,
+ LEF_LAYER_WIREEXT,
+ LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP,
+ LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT,
+ LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNA,
+ LEF_LAYER_ANTENNADIFF, LEF_LAYER_ANTENNASIDE,
+ LEF_LAYER_AGG_ANTENNA, LEF_LAYER_AGG_ANTENNADIFF,
+ LEF_LAYER_AGG_ANTENNASIDE,
+ LEF_LAYER_ACCURRENT, LEF_LAYER_DCCURRENT,
+ LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT,
+ LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE,
+ LEF_VIARULE_OVERHANG,
+ LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA,
+ LEF_VIARULE_GENERATE, LEF_LAYER_END};
+
+enum lef_spacing_keys {LEF_SPACING_RANGE=0, LEF_SPACING_BY,
+ LEF_SPACING_ADJACENTCUTS, LEF_END_LAYER_SPACING};
+
+void
+LefReadLayerSection(f, lname, mode, lefl)
+ FILE *f; /* LEF file being read */
+ char *lname; /* name of the layer */
+ int mode; /* layer, via, or viarule */
+ LefList lefl; /* pointer to layer info */
+{
+ char *token, *tp;
+ int keyword, typekey = -1, entries, i;
+ int curlayer = -1;
+ double dvalue, oscale;
+ LefList altVia;
+ lefSpacingRule *newrule = NULL, *testrule;
+
+ /* These are defined in the order of CLASS_* in lefInt.h */
+ static char *layer_type_keys[] = {
+ "ROUTING",
+ "CUT",
+ "MASTERSLICE",
+ "OVERLAP",
+ NULL
+ };
+
+ static char *layer_keys[] = {
+ "TYPE",
+ "WIDTH",
+ "MINWIDTH",
+ "MAXWIDTH",
+ "AREA",
+ "SPACING",
+ "SPACINGTABLE",
+ "PITCH",
+ "DIRECTION",
+ "OFFSET",
+ "WIREEXTENSION",
+ "RESISTANCE",
+ "CAPACITANCE",
+ "EDGECAPACITANCE",
+ "THICKNESS",
+ "HEIGHT",
+ "MINIMUMDENSITY",
+ "ANTENNAAREARATIO",
+ "ANTENNADIFFAREARATIO",
+ "ANTENNASIDEAREARATIO",
+ "ANTENNACUMAREARATIO",
+ "ANTENNACUMDIFFAREARATIO",
+ "ANTENNACUMSIDEAREARATIO",
+ "ACCURRENTDENSITY",
+ "DCCURRENTDENSITY",
+ "DEFAULT",
+ "LAYER",
+ "RECT",
+ "ENCLOSURE",
+ "PREFERENCLOSURE",
+ "OVERHANG",
+ "METALOVERHANG",
+ "VIA",
+ "GENERATE",
+ "END",
+ NULL
+ };
+
+ static char *spacing_keys[] = {
+ "RANGE",
+ "BY",
+ "ADJACENTCUTS",
+ ";",
+ NULL
+ };
+
+ /* Database is assumed to be in microns. */
+ /* If not, we need to parse the UNITS record, which is */
+ /* currently ignored. */
+
+ oscale = 1;
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, layer_keys);
+ if (keyword < 0)
+ {
+ LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
+ token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case LEF_LAYER_TYPE:
+ token = LefNextToken(f, TRUE);
+ if (*token != '\n')
+ {
+ typekey = Lookup(token, layer_type_keys);
+ if (typekey < 0)
+ LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; "
+ "ignoring.\n", token);
+ }
+ if (lefl->lefClass == CLASS_IGNORE) {
+ lefl->lefClass = typekey;
+ if (typekey == CLASS_ROUTE) {
+
+ lefl->info.route.width = 0.0;
+ lefl->info.route.spacing = NULL;
+ lefl->info.route.pitchx = 0.0;
+ lefl->info.route.pitchy = 0.0;
+ // Use -1.0 as an indication that offset has not
+ // been specified and needs to be set to default.
+ lefl->info.route.offsetx = -1.0;
+ lefl->info.route.offsety = -1.0;
+ lefl->info.route.hdirection = DIR_UNKNOWN;
+
+ lefl->info.route.minarea = 0.0;
+ lefl->info.route.thick = 0.0;
+ lefl->info.route.antenna = 0.0;
+ lefl->info.route.method = CALC_NONE;
+
+ lefl->info.route.areacap = 0.0;
+ lefl->info.route.respersq = 0.0;
+ lefl->info.route.edgecap = 0.0;
+
+ /* A routing type has been declared. Assume */
+ /* this takes the name "metal1", "M1", or some */
+ /* variant thereof. */
+
+ for (tp = lefl->lefName; *tp != '\0'; tp++) {
+ if (*tp >= '0' && *tp <= '9') {
+ sscanf(tp, "%d", &lefl->type);
+
+ /* "metal1", e.g., is assumed to be layer #0 */
+ /* This may not be a proper assumption, always */
+
+ lefl->type--;
+ break;
+ }
+ }
+
+ /* This is probably some special non-numerical */
+ /* name for a top metal layer. Take a stab at */
+ /* it, defining it to be the next layer up from */
+ /* whatever the previous topmost route layer */
+ /* was. This should work unless the LEF file */
+ /* is incorrectly written. */
+
+ if (lefl->type < 0) {
+ lefl->type = LefGetMaxRouteLayer();
+ }
+ }
+ else if (typekey == CLASS_CUT || typekey == CLASS_VIA) {
+ lefl->info.via.spacing = NULL;
+ lefl->info.via.area.x1 = 0.0;
+ lefl->info.via.area.y1 = 0.0;
+ lefl->info.via.area.x2 = 0.0;
+ lefl->info.via.area.y2 = 0.0;
+ lefl->info.via.area.layer = -1;
+ lefl->info.via.cell = (GATE)NULL;
+ lefl->info.via.lr = (DSEG)NULL;
+
+ /* Note: lefl->type not set here for cut */
+ /* layers so that route layers will all be */
+ /* clustered at the bottom. */
+ }
+ }
+ else if (lefl->lefClass != typekey) {
+ LefError(LEF_ERROR, "Attempt to reclassify layer %s from %s to %s\n",
+ lname, layer_type_keys[lefl->lefClass],
+ layer_type_keys[typekey]);
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_ACCURRENT:
+ // Not used, but syntax is f**king stupid and has to be
+ // specially handled, or it breaks the parser.
+ token = LefNextToken(f, TRUE); /* "AVERAGE", ... */
+ token = LefNextToken(f, TRUE); /* Value or "FREQUENCY" */
+ LefEndStatement(f);
+ if (!strcmp(token, "FREQUENCY"))
+ {
+ while (TRUE) {
+ token = LefNextToken(f, TRUE);
+ LefEndStatement(f);
+ if (!strcmp(token, "TABLEENTRIES")) {
+ break;
+ }
+ }
+ }
+ break;
+ case LEF_LAYER_DCCURRENT:
+ // Not used. See comments above for ACCURRENTDENSITY
+ token = LefNextToken(f, TRUE); /* "AVERAGE" */
+ token = LefNextToken(f, TRUE); /* Value or "WIDTH" */
+ LefEndStatement(f);
+ if (!strcmp(token, "WIDTH"))
+ {
+ while (TRUE) {
+ token = LefNextToken(f, TRUE);
+ LefEndStatement(f);
+ if (!strcmp(token, "TABLEENTRIES")) {
+ break;
+ }
+ }
+ }
+ break;
+ case LEF_LAYER_MINWIDTH:
+ // Not handled if width is already defined.
+ if ((lefl->lefClass != CLASS_ROUTE) ||
+ (lefl->info.route.width != 0)) {
+ LefEndStatement(f);
+ break;
+ }
+ /* drop through */
+ case LEF_LAYER_WIDTH:
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ if (lefl->lefClass == CLASS_ROUTE)
+ lefl->info.route.width = dvalue / (double)oscale;
+ else if (lefl->lefClass == CLASS_CUT) {
+ double baseval = (dvalue / (double)oscale) / 2.0;
+ lefl->info.via.area.x1 = -baseval;
+ lefl->info.via.area.y1 = -baseval;
+ lefl->info.via.area.x2 = baseval;
+ lefl->info.via.area.y2 = baseval;
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_MAXWIDTH:
+ // Not handled.
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_AREA:
+ /* Read minimum area rule value */
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ sscanf(token, "%lg", &dvalue);
+ // Units of area (length * length)
+ lefl->info.route.minarea = dvalue / (double)oscale / (double)oscale;
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_SPACING:
+ if ((lefl->lefClass != CLASS_ROUTE) && (lefl->lefClass != CLASS_CUT) &&
+ (lefl->lefClass != CLASS_VIA)) {
+ LefEndStatement(f);
+ break;
+ }
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ token = LefNextToken(f, TRUE);
+ typekey = Lookup(token, spacing_keys);
+
+ newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule));
+
+ // If no range specified, then the rule goes in front
+ if (typekey == LEF_SPACING_RANGE) {
+ // Get range minimum, ignore range maximum, and sort
+ // the spacing order.
+ newrule->spacing = dvalue / (double)oscale;
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ newrule->width = dvalue / (double)oscale;
+ for (testrule = lefl->info.route.spacing; testrule;
+ testrule = testrule->next)
+ if (testrule->next == NULL || testrule->next->width >
+ newrule->width)
+ break;
+
+ if (!testrule) {
+ newrule->next = NULL;
+ lefl->info.route.spacing = newrule;
+ }
+ else {
+ newrule->next = testrule->next;
+ testrule->next = newrule;
+ }
+ token = LefNextToken(f, TRUE);
+ typekey = Lookup(token, spacing_keys);
+ }
+ else if (typekey == LEF_SPACING_BY) {
+ /* In info.via.spacing, save two rules; first one */
+ /* is for X spacing, second is for Y spacing */
+
+ newrule->spacing = dvalue / (double)oscale;
+ newrule->width = 0;
+ newrule->next = NULL;
+ lefl->info.via.spacing = newrule;
+
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule));
+ newrule->spacing = dvalue / (double)oscale;
+ newrule->width = 0;
+ newrule->next = NULL;
+ lefl->info.via.spacing->next = newrule;
+ }
+ else if (typekey == LEF_SPACING_ADJACENTCUTS) {
+ /* ADJACENTCUTS not handled here (yet) */
+ /* Need this for power post generation in addspacers */
+ /* (Do nothing here) */
+ }
+ else if (typekey >= 0) {
+ newrule->spacing = dvalue / (double)oscale;
+ newrule->width = 0.0;
+ newrule->next = lefl->info.route.spacing;
+ lefl->info.route.spacing = newrule;
+ }
+ if (typekey != LEF_END_LAYER_SPACING)
+ LefEndStatement(f);
+ break;
+
+ case LEF_LAYER_SPACINGTABLE:
+ // Use the values for the maximum parallel runlength
+ token = LefNextToken(f, TRUE); // "PARALLELRUNLENTTH"
+ entries = 0;
+ while (1) {
+ token = LefNextToken(f, TRUE);
+ if (*token == ';' || !strcmp(token, "WIDTH"))
+ break;
+ else
+ entries++;
+ }
+ if (*token != ';')
+ newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule));
+
+ while (*token != ';') {
+ token = LefNextToken(f, TRUE); // Minimum width value
+ sscanf(token, "%lg", &dvalue);
+ newrule->width = dvalue / (double)oscale;
+
+ for (i = 0; i < entries; i++) {
+ token = LefNextToken(f, TRUE); // Spacing value
+ }
+ sscanf(token, "%lg", &dvalue);
+ newrule->spacing = dvalue / (double)oscale;
+ token = LefNextToken(f, TRUE);
+
+ for (testrule = lefl->info.route.spacing; testrule;
+ testrule = testrule->next)
+ if (testrule->next == NULL || testrule->next->width >
+ newrule->width)
+ break;
+
+ if (!testrule) {
+ newrule->next = NULL;
+ lefl->info.route.spacing = newrule;
+ }
+ else {
+ newrule->next = testrule->next;
+ testrule->next = newrule;
+ }
+ token = LefNextToken(f, TRUE);
+ if (strcmp(token, "WIDTH")) break;
+ }
+ break;
+ case LEF_LAYER_PITCH:
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ lefl->info.route.pitchx = dvalue / (double)oscale;
+
+ token = LefNextToken(f, TRUE);
+ if (token && (*token != ';')) {
+ sscanf(token, "%lg", &dvalue);
+ lefl->info.route.pitchy = dvalue / (double)oscale;
+ LefEndStatement(f);
+ }
+ else {
+ lefl->info.route.pitchy = lefl->info.route.pitchx;
+ /* If the orientation is known, then zero the pitch */
+ /* in the opposing direction. If not, then set the */
+ /* direction to DIR_RESOLVE so that the pitch in */
+ /* the opposing direction can be zeroed when the */
+ /* direction is specified. */
+
+ if (lefl->info.route.hdirection == DIR_UNKNOWN)
+ lefl->info.route.hdirection = DIR_RESOLVE;
+ else if (lefl->info.route.hdirection == DIR_VERTICAL)
+ lefl->info.route.pitchy = 0.0;
+ else if (lefl->info.route.hdirection == DIR_HORIZONTAL)
+ lefl->info.route.pitchx = 0.0;
+ }
+
+ /* Offset default is 1/2 the pitch. Offset is */
+ /* intialized to -1 to tell whether or not the value */
+ /* has been set by an OFFSET statement. */
+ if (lefl->info.route.offsetx < 0.0)
+ lefl->info.route.offsetx = lefl->info.route.pitchx / 2.0;
+ if (lefl->info.route.offsety < 0.0)
+ lefl->info.route.offsety = lefl->info.route.pitchy / 2.0;
+ break;
+ case LEF_LAYER_DIRECTION:
+ token = LefNextToken(f, TRUE);
+ LefLower(token);
+ if (lefl->info.route.hdirection == DIR_RESOLVE) {
+ if (token[0] == 'h')
+ lefl->info.route.pitchx = 0.0;
+ else if (token[0] == 'v')
+ lefl->info.route.pitchy = 0.0;
+ }
+ lefl->info.route.hdirection = (token[0] == 'h') ? DIR_HORIZONTAL
+ : DIR_VERTICAL;
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_OFFSET:
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ lefl->info.route.offsetx = dvalue / (double)oscale;
+
+ token = LefNextToken(f, TRUE);
+ if (token && (*token != ';')) {
+ sscanf(token, "%lg", &dvalue);
+ lefl->info.route.offsety = dvalue / (double)oscale;
+ LefEndStatement(f);
+ }
+ else {
+ lefl->info.route.offsety = lefl->info.route.offsetx;
+ }
+ break;
+ case LEF_LAYER_RES:
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ if (!strcmp(token, "RPERSQ")) {
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ // Units are ohms per square
+ lefl->info.route.respersq = dvalue;
+ }
+ }
+ else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) {
+ sscanf(token, "%lg", &dvalue);
+ lefl->info.via.respervia = dvalue; // Units ohms
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_CAP:
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ if (!strcmp(token, "CPERSQDIST")) {
+ token = LefNextToken(f, TRUE);
+ sscanf(token, "%lg", &dvalue);
+ // Units are pF per squared unit length
+ lefl->info.route.areacap = dvalue /
+ ((double)oscale * (double)oscale);
+ }
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_EDGECAP:
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ sscanf(token, "%lg", &dvalue);
+ // Units are pF per unit length
+ lefl->info.route.edgecap = dvalue / (double)oscale;
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_THICKNESS:
+ case LEF_LAYER_HEIGHT:
+ /* Assuming thickness and height are the same thing? */
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ sscanf(token, "%lg", &dvalue);
+ // Units of length
+ lefl->info.route.thick = dvalue / (double)oscale;
+ }
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_ANTENNA:
+ case LEF_LAYER_ANTENNASIDE:
+ case LEF_LAYER_AGG_ANTENNA:
+ case LEF_LAYER_AGG_ANTENNASIDE:
+ /* NOTE: Assuming that only one of these methods will */
+ /* be used! If more than one is present, then only the */
+ /* last one will be recorded and used. */
+
+ token = LefNextToken(f, TRUE);
+ if (lefl->lefClass == CLASS_ROUTE) {
+ sscanf(token, "%lg", &dvalue);
+ // Unitless values (ratio)
+ lefl->info.route.antenna = dvalue;
+ }
+ if (keyword == LEF_LAYER_ANTENNA)
+ lefl->info.route.method = CALC_AREA;
+ else if (keyword == LEF_LAYER_ANTENNASIDE)
+ lefl->info.route.method = CALC_SIDEAREA;
+ else if (keyword == LEF_LAYER_AGG_ANTENNA)
+ lefl->info.route.method = CALC_AGG_AREA;
+ else
+ lefl->info.route.method = CALC_AGG_SIDEAREA;
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_ANTENNADIFF:
+ case LEF_LAYER_AGG_ANTENNADIFF:
+ /* Not specifically handling these antenna types */
+ /* (antenna ratios for antennas connected to diodes, */
+ /* which can still blow gates if the diode area is */
+ /* insufficiently large.) */
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_MINDENSITY:
+ case LEF_LAYER_WIREEXT:
+ /* Not specifically handling these */
+ LefEndStatement(f);
+ break;
+ case LEF_VIA_DEFAULT:
+ case LEF_VIARULE_GENERATE:
+ /* Do nothing; especially, don't look for end-of-statement! */
+ break;
+ case LEF_VIA_LAYER:
+ curlayer = LefReadLayer(f, FALSE);
+ LefEndStatement(f);
+ break;
+ case LEF_VIA_RECT:
+ if (curlayer >= 0)
+ LefAddViaGeometry(f, lefl, curlayer, oscale);
+ LefEndStatement(f);
+ break;
+ case LEF_VIA_ENCLOSURE:
+ /* Defines how to draw via metal layers. */
+ /* Note that values can interact with ENCLOSURE */
+ /* values given for the cut layer type. This is */
+ /* not being handled. */
+ {
+ DSEG viarect, encrect;
+ encrect = LefReadEnclosure(f, curlayer, oscale);
+ viarect = (DSEG)malloc(sizeof(struct dseg_));
+ *viarect = *encrect;
+ viarect->next = lefl->info.via.lr;
+ lefl->info.via.lr = viarect;
+ }
+ if (mode == LEF_SECTION_VIARULE)
+ lefl->info.via.generated = TRUE;
+ LefEndStatement(f);
+ break;
+ case LEF_VIARULE_OVERHANG:
+ case LEF_VIARULE_METALOVERHANG:
+ /* These are from older LEF definitions (e.g., 5.4) */
+ /* and cannot completely specify via geometry. So if */
+ /* seen, ignore the rest of the via section. Only the */
+ /* explicitly defined VIA types will be used. */
+ LefError(LEF_WARNING, "NOTE: Old format VIARULE ignored.\n");
+ lefl->lefClass == CLASS_IGNORE;
+ LefEndStatement(f);
+ /* LefSkipSection(f, lname); */ /* Continue parsing */
+ break;
+ case LEF_VIA_PREFERENCLOSURE:
+ /* Ignoring this. */
+ LefEndStatement(f);
+ break;
+ case LEF_VIARULE_VIA:
+ LefEndStatement(f);
+ break;
+ case LEF_LAYER_END:
+ if (!LefParseEndStatement(f, lname))
+ {
+ LefError(LEF_ERROR, "Layer END statement missing.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == LEF_LAYER_END) break;
+ }
+}
+
+/*
+ *------------------------------------------------------------
+ *
+ * LefRead --
+ *
+ * Read a .lef file and generate all routing configuration
+ * structures and values from the LAYER, VIA, and MACRO sections
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * Many. Cell definitions are created and added to
+ * the GateInfo database.
+ *
+ *------------------------------------------------------------
+ */
+
+/* See above for "enum lef_sections {...}" */
+
+int
+LefRead(inName)
+ char *inName;
+{
+ FILE *f;
+ char filename[256];
+ char *token;
+ char tsave[128];
+ int keyword, layer;
+ int oprecis = 100; // = 1 / manufacturing grid (microns)
+ float oscale;
+ double xydiff, ogrid, minwidth;
+ LefList lefl;
+ DSEG grect;
+ GATE gateginfo;
+
+ static char *sections[] = {
+ "VERSION",
+ "BUSBITCHARS",
+ "DIVIDERCHAR",
+ "MANUFACTURINGGRID",
+ "USEMINSPACING",
+ "CLEARANCEMEASURE",
+ "NAMESCASESENSITIVE",
+ "PROPERTYDEFINITIONS",
+ "UNITS",
+ "LAYER",
+ "VIA",
+ "VIARULE",
+ "SPACING",
+ "SITE",
+ "PROPERTY",
+ "NOISETABLE",
+ "CORRECTIONTABLE",
+ "IRDROP",
+ "ARRAY",
+ "TIMING",
+ "BEGINEXT",
+ "MACRO",
+ "END",
+ NULL
+ };
+
+ if (!strrchr(inName, '.'))
+ sprintf(filename, "%s.lef", inName);
+ else
+ strcpy(filename, inName);
+
+ f = fopen(filename, "r");
+
+ if (f == NULL)
+ {
+ fprintf(stderr, "Cannot open input file: ");
+ perror(filename);
+ return 0;
+ }
+
+ if (Verbose > 0) {
+ fprintf(stdout, "Reading LEF data from file %s.\n", filename);
+ fflush(stdout);
+ }
+
+ oscale = 1;
+
+ LefHashInit();
+
+ while ((token = LefNextToken(f, TRUE)) != NULL)
+ {
+ keyword = Lookup(token, sections);
+ if (keyword < 0)
+ {
+ LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n",
+ token);
+ LefEndStatement(f);
+ continue;
+ }
+ switch (keyword)
+ {
+ case LEF_VERSION:
+ case LEF_DIVIDERCHAR:
+ case LEF_CLEARANCEMEASURE:
+ case LEF_USEMINSPACING:
+ case LEF_NAMESCASESENSITIVE:
+ LefEndStatement(f);
+ break;
+ case LEF_BUSBITCHARS:
+ token = LefNextToken(f, TRUE);
+ delimiter = (*token == '\"') ? *(token + 1) : *token;
+ LefEndStatement(f);
+ break;
+ case LEF_MANUFACTURINGGRID:
+ token = LefNextToken(f, TRUE);
+ if (sscanf(token, "%lg", &ogrid) == 1)
+ oprecis = (int)((1.0 / ogrid) + 0.5);
+ LefEndStatement(f);
+ break;
+ case LEF_PROPERTYDEFS:
+ LefSkipSection(f, sections[LEF_PROPERTYDEFS]);
+ break;
+ case LEF_UNITS:
+ LefSkipSection(f, sections[LEF_UNITS]);
+ break;
+
+ case LEF_SECTION_VIA:
+ case LEF_SECTION_VIARULE:
+ token = LefNextToken(f, TRUE);
+ sprintf(tsave, "%.127s", token);
+
+ lefl = LefFindLayer(token);
+
+ if (keyword == LEF_SECTION_VIARULE) {
+ char *vianame = (char *)malloc(strlen(token) + 3);
+ sprintf(vianame, "%s_0", token);
+
+ /* If the following keyword is GENERATE, then */
+ /* prepare up to four contact types to represent */
+ /* all possible orientations of top and bottom */
+ /* metal layers. If no GENERATE keyword, ignore. */
+
+ token = LefNextToken(f, TRUE);
+ if (!strcmp(token, "GENERATE")) {
+ lefl = LefNewVia(vianame);
+ lefl->next = LefInfo;
+ LefInfo = lefl;
+ LefReadLayerSection(f, tsave, keyword, lefl);
+ }
+ else {
+ LefSkipSection(f, tsave);
+ }
+ free(vianame);
+ }
+ else if (lefl == NULL)
+ {
+ lefl = LefNewVia(token);
+ lefl->next = LefInfo;
+ LefInfo = lefl;
+ LefReadLayerSection(f, tsave, keyword, lefl);
+ }
+ else
+ {
+ LefError(LEF_WARNING, "Warning: Cut type \"%s\" redefined.\n",
+ token);
+ lefl = LefRedefined(lefl, token);
+ LefReadLayerSection(f, tsave, keyword, lefl);
+ }
+ break;
+
+ case LEF_SECTION_LAYER:
+ token = LefNextToken(f, TRUE);
+ sprintf(tsave, "%.127s", token);
+
+ lefl = LefFindLayer(token);
+ if (lefl == (LefList)NULL)
+ {
+ lefl = LefNewRoute(token);
+ lefl->next = LefInfo;
+ LefInfo = lefl;
+ }
+ else
+ {
+ if (lefl && lefl->type < 0)
+ {
+ LefError(LEF_ERROR, "Layer %s is only defined for"
+ " obstructions!\n", token);
+ LefSkipSection(f, tsave);
+ break;
+ }
+ }
+ LefReadLayerSection(f, tsave, keyword, lefl);
+ break;
+
+ case LEF_SECTION_SPACING:
+ LefSkipSection(f, sections[LEF_SECTION_SPACING]);
+ break;
+ case LEF_SECTION_SITE:
+ token = LefNextToken(f, TRUE);
+ if (Verbose > 0)
+ fprintf(stdout, "LEF file: Defines site %s\n", token);
+ sprintf(tsave, "site_%.122s", token);
+ LefReadMacro(f, tsave, oscale);
+ break;
+ case LEF_PROPERTY:
+ LefSkipSection(f, NULL);
+ break;
+ case LEF_NOISETABLE:
+ LefSkipSection(f, sections[LEF_NOISETABLE]);
+ break;
+ case LEF_CORRECTIONTABLE:
+ LefSkipSection(f, sections[LEF_CORRECTIONTABLE]);
+ break;
+ case LEF_IRDROP:
+ LefSkipSection(f, sections[LEF_IRDROP]);
+ break;
+ case LEF_ARRAY:
+ LefSkipSection(f, sections[LEF_ARRAY]);
+ break;
+ case LEF_SECTION_TIMING:
+ LefSkipSection(f, sections[LEF_SECTION_TIMING]);
+ break;
+ case LEF_EXTENSION:
+ LefSkipSection(f, sections[LEF_EXTENSION]);
+ break;
+ case LEF_MACRO:
+ token = LefNextToken(f, TRUE);
+ /* Diagnostic */
+ /*
+ fprintf(stdout, "LEF file: Defines new cell %s\n", token);
+ */
+ sprintf(tsave, "%.127s", token);
+ LefReadMacro(f, tsave, oscale);
+ break;
+ case LEF_END:
+ if (!LefParseEndStatement(f, "LIBRARY"))
+ {
+ LefError(LEF_ERROR, "END statement out of context.\n");
+ keyword = -1;
+ }
+ break;
+ }
+ if (keyword == LEF_END) break;
+ }
+ if (Verbose > 0) {
+ fprintf(stdout, "LEF read: Processed %d lines.\n", lefCurrentLine);
+ LefError(LEF_ERROR, NULL); /* print statement of errors, if any */
+ }
+
+ /* Cleanup */
+ if (f != NULL) fclose(f);
+
+ /* Make sure that the gate list has one entry called "pin" */
+
+ for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next)
+ if (!strcasecmp(gateginfo->gatename, "pin"))
+ break;
+
+ if (!gateginfo) {
+ /* Add a new GateInfo entry for pseudo-gate "pin" */
+ gateginfo = (GATE)malloc(sizeof(struct gate_));
+ gateginfo->gatetype = NULL;
+ gateginfo->gateclass = MACRO_CLASS_DEFAULT;
+ gateginfo->gatesubclass = MACRO_SUBCLASS_NONE;
+ gateginfo->gatename = (char *)malloc(4);
+ strcpy(gateginfo->gatename, "pin");
+ gateginfo->width = 0.0;
+ gateginfo->height = 0.0;
+ gateginfo->placedX = 0.0;
+ gateginfo->placedY = 0.0;
+ gateginfo->nodes = 1;
+
+ gateginfo->taps = (DSEG *)malloc(sizeof(DSEG));
+ gateginfo->noderec = (NODE *)malloc(sizeof(NODE));
+ gateginfo->area = (float *)malloc(sizeof(float));
+ gateginfo->direction = (u_char *)malloc(sizeof(u_char));
+ gateginfo->use = (u_char *)malloc(sizeof(u_char));
+ gateginfo->netnum = (int *)malloc(sizeof(int));
+ gateginfo->node = (char **)malloc(sizeof(char *));
+
+ grect = (DSEG)malloc(sizeof(struct dseg_));
+ grect->x1 = grect->x2 = 0.0;
+ grect->y1 = grect->y2 = 0.0;
+ grect->next = (DSEG)NULL;
+ gateginfo->obs = (DSEG)NULL;
+ gateginfo->next = GateInfo;
+ gateginfo->last = (GATE)NULL;
+ gateginfo->taps[0] = grect;
+ gateginfo->noderec[0] = NULL;
+ gateginfo->area[0] = 0.0;
+ gateginfo->netnum[0] = -1;
+ gateginfo->node[0] = strdup("pin");
+ gateginfo->clientdata = (void *)NULL;
+ GateInfo = gateginfo;
+
+ LefHashMacro(gateginfo);
+ }
+ return oprecis;
+}
diff --git a/src/readlef.h b/src/readlef.h
new file mode 100644
index 0000000..39edd6b
--- /dev/null
+++ b/src/readlef.h
@@ -0,0 +1,220 @@
+/*
+ * readlef.h --
+ *
+ * This file defines things that are used by internal LEF routines in
+ * various files.
+ *
+ */
+
+#ifndef _READLEF_H
+#define _READLEF_H
+
+#include "lef.h"
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* Some constants for LEF and DEF files */
+
+#define LEF_LINE_MAX 2048 /* Maximum length fixed by LEF/DEF specifications */
+#define LEF_MAX_ERRORS 100 /* Max # errors to report; limits output if */
+ /* something is really wrong about the file */
+
+#define DEFAULT_WIDTH 3 /* Default metal width for routes if undefined */
+#define DEFAULT_SPACING 4 /* Default spacing between metal if undefined */
+
+/* Structure holding the counts of regular and special nets */
+
+typedef struct
+{
+ int regular;
+ int special;
+ u_char has_nets;
+} NetCount;
+
+/* Various modes for writing nets. */
+#define DO_REGULAR 0
+#define DO_SPECIAL 1
+#define ALL_SPECIAL 2 /* treat all nets as SPECIALNETS */
+
+/* Types of error messages */
+enum lef_error_types {LEF_ERROR = 0, LEF_WARNING, DEF_ERROR, DEF_WARNING};
+
+/* Macro classes */
+enum macro_classes {MACRO_CLASS_DEFAULT = 0, MACRO_CLASS_CORE,
+ MACRO_CLASS_BLOCK, MACRO_CLASS_PAD, MACRO_CLASS_RING,
+ MACRO_CLASS_COVER, MACRO_CLASS_ENDCAP};
+
+/* Core macro subclasses */
+enum macro_subclasses {MACRO_SUBCLASS_NONE = 0,
+ MACRO_SUBCLASS_SPACER, MACRO_SUBCLASS_ANTENNA,
+ MACRO_SUBCLASS_WELLTAP, MACRO_SUBCLASS_TIEHIGH,
+ MACRO_SUBCLASS_TIELOW, MACRO_SUBCLASS_FEEDTHRU};
+
+/* Port classes */
+enum port_classes {PORT_CLASS_DEFAULT = 0, PORT_CLASS_INPUT,
+ PORT_CLASS_OUTPUT, PORT_CLASS_TRISTATE, PORT_CLASS_BIDIRECTIONAL,
+ PORT_CLASS_FEEDTHROUGH};
+
+/* Port uses */
+enum port_uses {PORT_USE_DEFAULT = 0, PORT_USE_SIGNAL,
+ PORT_USE_ANALOG, PORT_USE_POWER, PORT_USE_GROUND,
+ PORT_USE_CLOCK, PORT_USE_TIEOFF, PORT_USE_SCAN,
+ PORT_USE_RESET};
+
+/* Structure to hold information about spacing rules */
+typedef struct _lefSpacingRule *lefSpacingPtr;
+typedef struct _lefSpacingRule {
+ lefSpacingPtr next;
+ double width; /* width, in microns */
+ double spacing; /* minimum spacing rule, in microns */
+} lefSpacingRule;
+
+/* Area calculation methods for finding antenna ratios. ANTENNA_ROUTE */
+/* is not a method but is used to indicate to the antenna search */
+/* routine that the search is for routing preparation and not for */
+/* calculating error. */
+
+enum area_methods {CALC_NONE = 0, CALC_AREA, CALC_SIDEAREA, CALC_AGG_AREA,
+ CALC_AGG_SIDEAREA, ANTENNA_ROUTE, ANTENNA_DISABLE};
+
+/* Structure used to maintain default routing information for each */
+/* routable layer type. */
+
+typedef struct {
+ lefSpacingRule *spacing; /* spacing rules, ordered by width */
+ double width; /* nominal route width, in microns */
+ double pitchx; /* route X pitch, in microns */
+ double pitchy; /* route Y pitch, in microns */
+ double offsetx; /* route track offset from X origin, in microns */
+ double offsety; /* route track offset from Y origin, in microns */
+ double respersq; /* resistance per square */
+ double areacap; /* area capacitance per square micron */
+ double edgecap; /* edge capacitance per micron */
+ double minarea; /* minimum metal area rule */
+ double thick; /* metal layer thickness, if given */
+ double antenna; /* antenna area ratio rule */
+ u_char method; /* antenna rule calculation method */
+ u_char hdirection; /* horizontal direction preferred */
+} lefRoute;
+
+/* These values may be used for "hdirection". initialize hdirection */
+/* with DIR_UNKNOWN. If the LEF file defines the pitch before the */
+/* direction and does not specify both X and Y pitches, then change to */
+/* DIR_RESOLVE to indicate that the pitch in the non-preferred */
+/* direction should be zeroed when the preferred direction is known. */
+
+#define DIR_VERTICAL (u_char)0
+#define DIR_HORIZONTAL (u_char)1
+#define DIR_UNKNOWN (u_char)2
+#define DIR_RESOLVE (u_char)3
+
+/* Structure used to maintain default generation information for each */
+/* via or viarule (contact) type. If "cell" is non-NULL, then the via */
+/* is saved in a cell (pointed to by "cell"), and "area" describes the */
+/* bounding box. Otherwise, the via is formed by magic type "type" */
+/* with a minimum area "area" for a single contact. */
+
+typedef struct {
+ lefSpacingRule *spacing; /* spacing rules, ordered by width */
+ struct dseg_ area; /* Area of single contact, or cell bbox */
+ /* in units of microns */
+ GATE cell; /* Cell for fixed via def, or NULL */
+ DSEG lr; /* Extra information for vias with */
+ /* more complicated geometry. */
+ double respervia; /* resistance per via */
+ int obsType; /* Secondary obstruction type */
+ char generated; /* Flag indicating via from VIARULE */
+} lefVia;
+
+/* Defined types for "lefClass" in the lefLayer structure */
+/* Note that the first four match TYPE records in the LEF. IGNORE has */
+/* a special meaning to qrouter, and VIA is for VIA definitions. */
+
+#define CLASS_ROUTE 0 /* route layer */
+#define CLASS_CUT 1 /* cut layer */
+#define CLASS_MASTER 2 /* masterslice layer */
+#define CLASS_OVERLAP 3 /* overlap layer */
+#define CLASS_IGNORE 4 /* inactive layer */
+#define CLASS_VIA 5 /* via record */
+
+/* Structure defining a route or via layer and matching it to a magic */
+/* layer type. This structure is saved in the LefInfo list. */
+
+typedef struct _lefLayer *LefList;
+
+typedef struct _lefLayer {
+ LefList next; /* Next layer in linked list */
+ char *lefName; /* CIF name of this layer */
+ int type; /* GDS layer type, or -1 for none */
+ int obsType; /* GDS type to use if this is an obstruction */
+ u_char lefClass; /* is this a via, route, or masterslice layer */
+ union {
+ lefRoute route; /* for route layers */
+ lefVia via; /* for contacts */
+ } info;
+} lefLayer;
+
+/* External declaration of global variables */
+extern int lefCurrentLine;
+extern LefList LefInfo;
+extern LinkedStringPtr AllowedVias;
+
+/* Forward declarations */
+
+int Lookup(char *str, char*(table[]));
+
+u_char LefParseEndStatement(FILE *f, char *match);
+void LefSkipSection(FILE *f, char *match);
+void LefEndStatement(FILE *f);
+GATE lefFindCell(char *name);
+char *LefNextToken(FILE *f, u_char ignore_eol);
+char *LefLower(char *token);
+DSEG LefReadGeometry(GATE lefMacro, FILE *f, float oscale);
+LefList LefRedefined(LefList lefl, char *redefname);
+void LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale);
+DSEG LefReadRect(FILE *f, int curlayer, float oscale);
+int LefReadLayer(FILE *f, u_char obstruct);
+LefList LefFindLayer(char *token);
+LefList LefFindLayerByNum(int layer);
+LefList LefNewVia(char *name);
+int LefFindLayerNum(char *token);
+void LefSetRoutePitchX(int layer, double value);
+void LefSetRoutePitchY(int layer, double value);
+double LefGetViaWidth(LefList lefl, int layer, int dir);
+double LefGetRouteKeepout(int layer);
+double LefGetRouteWidth(int layer);
+double LefGetRouteMinArea(int layer);
+double LefGetRouteSpacing(int layer);
+double LefGetRouteWideSpacing(int layer, double width);
+double LefGetRoutePitch(int layer);
+double LefGetRoutePitchX(int layer);
+double LefGetRoutePitchY(int layer);
+double LefGetRouteOffset(int layer);
+double LefGetRouteThickness(int layer);
+double LefGetRouteAreaRatio(int layer);
+u_char LefGetRouteAntennaMethod(int layer);
+int LefGetRouteRCvalues(int layer, double *areacap, double *edgecap,
+ double *respersq);
+int LefGetViaResistance(int layer, double *respervia);
+char *LefGetRouteName(int layer);
+int LefGetRouteOrientation(int layer);
+int LefGetMaxRouteLayer(void);
+int LefGetMaxLayer(void);
+
+GATE LefFindInstance(char *name);
+void LefHashCell(GATE gateginfo);
+
+int LefRead(char *inName);
+void LefAssignLayerVias();
+void LefWriteGeneratedVias(FILE *f, double oscale, int defvias);
+
+
+void LefError(int type, char *fmt, ...); /* Variable argument procedure */
+ /* requires parameter list. */
+
+#endif /* _READLEF_H */
diff --git a/src/readliberty.c b/src/readliberty.c
index 079a644..4505480 100644
--- a/src/readliberty.c
+++ b/src/readliberty.c
@@ -167,6 +167,7 @@ advancetoken(FILE *flib, char delimiter)
// Remove any trailing whitespace
tptr = token + strlen(token) - 1;
+ if (tptr < token) tptr = token;
while (isblank(*tptr)) {
*tptr = '\0';
tptr--;
@@ -650,16 +651,24 @@ read_liberty(char *libfile, char *pattern)
newtable->invert = 1;
}
else if (!strcasecmp(token, "index_1")) {
+ char dnum = ',';
+
token = advancetoken(flib, 0); // Open parens
token = advancetoken(flib, 0); // Quote
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ iptr = token;
+
+ // Check if table is comma or space separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (newtable->invert == 1) {
// Count entries
- iptr = token;
newtable->csize = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->csize++;
}
@@ -669,7 +678,7 @@ read_liberty(char *libfile, char *pattern)
iptr = token;
sscanf(iptr, "%lg", &newtable->caps[0]);
newtable->caps[0] *= cap_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->csize++;
sscanf(iptr, "%lg",
@@ -679,10 +688,10 @@ read_liberty(char *libfile, char *pattern)
newtable->csize++;
}
else { // newtable->invert = 0
+
// Count entries
- iptr = token;
newtable->tsize = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->tsize++;
}
@@ -692,7 +701,7 @@ read_liberty(char *libfile, char *pattern)
iptr = token;
sscanf(iptr, "%lg", &newtable->times[0]);
newtable->times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->tsize++;
sscanf(iptr, "%lg",
@@ -705,16 +714,26 @@ read_liberty(char *libfile, char *pattern)
token = advancetoken(flib, ';'); // EOL semicolon
}
else if (!strcasecmp(token, "index_2")) {
+ char dnum = ',';
+
token = advancetoken(flib, 0); // Open parens
token = advancetoken(flib, 0); // Quote
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ // Determine if array tokens are comma or space separated.
+ iptr = token;
+
+ // Check if table is comma or space separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (newtable->invert == 0) {
+
// Count entries
- iptr = token;
newtable->csize = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->csize++;
}
@@ -724,7 +743,7 @@ read_liberty(char *libfile, char *pattern)
iptr = token;
sscanf(iptr, "%lg", &newtable->caps[0]);
newtable->caps[0] *= cap_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->csize++;
sscanf(iptr, "%lg",
@@ -735,9 +754,8 @@ read_liberty(char *libfile, char *pattern)
}
else { // newtable->invert == 1
// Count entries
- iptr = token;
newtable->tsize = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->tsize++;
}
@@ -747,7 +765,7 @@ read_liberty(char *libfile, char *pattern)
iptr = token;
sscanf(iptr, "%lg", &newtable->times[0]);
newtable->times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->tsize++;
sscanf(iptr, "%lg",
@@ -1152,6 +1170,7 @@ read_liberty(char *libfile, char *pattern)
while (*token != '}') {
token = advancetoken(flib, 0);
if (!strcasecmp(token, "index_1")) {
+ char dnum = ',';
// Local index values override those in the template
@@ -1162,15 +1181,21 @@ read_liberty(char *libfile, char *pattern)
//-------------------------
+ iptr = token;
+
+ // Check if table is comma or space separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (reftable && (reftable->invert == 1)) {
// Entries had better match the ref table
- iptr = token;
i = 0;
newcell->caps = (double *)malloc(reftable->csize *
sizeof(double));
sscanf(iptr, "%lg", &newcell->caps[0]);
newcell->caps[0] *= cap_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &newcell->caps[i]);
@@ -1178,13 +1203,12 @@ read_liberty(char *libfile, char *pattern)
}
}
else if (reftable && (reftable->invert == 0)) {
- iptr = token;
i = 0;
newcell->times = (double *)malloc(reftable->tsize *
sizeof(double));
sscanf(iptr, "%lg", &newcell->times[0]);
newcell->times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
if (i < reftable->tsize) {
@@ -1201,6 +1225,7 @@ read_liberty(char *libfile, char *pattern)
token = advancetoken(flib, ';'); // EOL semicolon
}
else if (!strcasecmp(token, "index_2")) {
+ char dnum = ',';
// Local index values override those in the template
@@ -1211,15 +1236,21 @@ read_liberty(char *libfile, char *pattern)
//-------------------------
+ iptr = token;
+
+ // Check if table is comma or space separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (reftable && (reftable->invert == 1)) {
// Entries had better match the ref table
- iptr = token;
i = 0;
newcell->times = (double *)malloc(reftable->tsize *
sizeof(double));
sscanf(iptr, "%lg", &newcell->times[0]);
newcell->times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &newcell->times[i]);
@@ -1227,13 +1258,12 @@ read_liberty(char *libfile, char *pattern)
}
}
else if (reftable && (reftable->invert == 0)) {
- iptr = token;
i = 0;
newcell->caps = (double *)malloc(reftable->csize *
sizeof(double));
sscanf(iptr, "%lg", &newcell->caps[0]);
newcell->caps[0] *= cap_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
if (i < reftable->csize) {
@@ -1274,6 +1304,7 @@ read_liberty(char *libfile, char *pattern)
*(newcell->values + j * reftable->tsize
+ i) = gval * time_unit;
while (*iptr != ' ' && *iptr != '\"' &&
+ *iptr != '\0' &&
*iptr != ',' || *iptr == '\\')
iptr++;
}
@@ -1390,7 +1421,12 @@ get_values(Cell *curcell, double *retdelay, double *retcap)
// Calculate delay per load. Note that cap at this point should be
// in fF, and trise should be in ps.
// So the value of loaddelay is in ps/fF.
- loaddelay = (maxtrise - mintrise) / (maxcap - mincap);
+ // If there is only one cap value, then nothing can be calculated; set
+ // loaddelay to zero so it does not blow up to infinity.
+ if (maxcap == mincap)
+ loaddelay = 0.0;
+ else
+ loaddelay = (maxtrise - mintrise) / (maxcap - mincap);
curcell->slope = loaddelay;
curcell->mintrans = mintrise;
@@ -1472,6 +1508,7 @@ Pin *
get_pin_by_name(Cell *curcell, char *pinname)
{
Pin *curpin;
+ char *dptr;
for (curpin = curcell->pins; curpin; curpin = curpin->next) {
if (!strcmp(curpin->name, pinname)) {
@@ -1479,6 +1516,18 @@ get_pin_by_name(Cell *curcell, char *pinname)
return curpin;
}
}
+ /* Check for buses */
+ for (curpin = curcell->pins; curpin; curpin = curpin->next) {
+ dptr = strchr(curpin->name, '[');
+ if (dptr != NULL) {
+ *dptr = '\0';
+ if (!strcmp(curpin->name, pinname)) {
+ *dptr = '[';
+ return curpin;
+ }
+ *dptr = '[';
+ }
+ }
return NULL;
}
diff --git a/src/readliberty.h b/src/readliberty.h
index 72add1a..d84050b 100644
--- a/src/readliberty.h
+++ b/src/readliberty.h
@@ -17,6 +17,8 @@
#define PIN_INPUT 0
#define PIN_CLOCK 1
#define PIN_OUTPUT 2
+#define PIN_POWER 3
+#define PIN_GROUND 4
// Function translation
#define GROUPBEGIN 1
diff --git a/src/readverilog.c b/src/readverilog.c
new file mode 100644
index 0000000..30b2ba4
--- /dev/null
+++ b/src/readverilog.c
@@ -0,0 +1,1989 @@
+/*----------------------------------------------------------------------*/
+/* readverilog.c -- Input for Verilog format (structural verilog only) */
+/* */
+/* Adapted from verilog.c in netgen-1.5 */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/* The verilog input is limited to "structural verilog", that is, */
+/* verilog code that only defines inputs, outputs, local nodes (via the */
+/* "wire" statement), and instanced modules. All modules are read down */
+/* to the point where either a module (1) does not conform to the */
+/* requirements above, or (2) has no module definition, in which case */
+/* it is treated as a "black box" subcircuit and therefore becomes a */
+/* low-level device. Because in verilog syntax all instances of a */
+/* module repeat both the module pin names and the local connections, */
+/* placeholders can be built without the need to redefine pins later, */
+/* as must be done for formats like SPICE that don't declare pin names */
+/* in an instance call. */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/* Note that use of 1'b0 or 1'b1 and similar variants is prohibited; */
+/* the structural netlist should either declare VSS and/or VDD as */
+/* inputs, or use tie-high and tie-low standard cells. */
+/*----------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------*/
+/* Most verilog syntax has been captured below. Still to do: Handle */
+/* wires that are created on the fly using {...} notation, including */
+/* the {n{...}} concatenation method. Currently this syntax is only */
+/* handled in nets connected to instance pins. */
+/*----------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "hash.h"
+#include "readverilog.h"
+
+/*------------------------------------------------------*/
+/* Global variables */
+/*------------------------------------------------------*/
+
+struct filestack *OpenFiles = NULL;
+int linesize = 0; /* Amount of memory allocated for line */
+int vlinenum = 0;
+char *nexttok;
+char *linetok;
+char *line = NULL; /* The line read in */
+FILE *infile = NULL;
+
+struct hashtable verilogparams;
+struct hashtable verilogdefs;
+struct hashtable verilogvectors;
+
+char dictinit = FALSE;
+
+/*------------------------------------------------------*/
+/* Structure for stacking nested `if[n]def */
+/*------------------------------------------------------*/
+
+struct ifstack {
+ int invert;
+ char *kl;
+ struct ifstack *next;
+};
+
+struct ifstack *condstack = NULL;
+
+/*------------------------------------------------------*/
+/* Stack handling (push and pop) */
+/*------------------------------------------------------*/
+
+void PushStack(struct cellrec *cell, struct cellstack **top)
+{
+ struct cellstack *newstack;
+
+ newstack = (struct cellstack *)calloc(1, sizeof(struct cellstack));
+ newstack->cell = cell;
+ newstack->next = *top;
+ *top = newstack;
+}
+
+/*------------------------------------------------------*/
+
+struct cellrec *PopStack(struct cellstack **top)
+{
+ struct cellstack *stackptr;
+ struct cellrec *cellptr;
+
+ stackptr = *top;
+ cellptr = stackptr->cell;
+ if (!stackptr) return NULL;
+ *top = stackptr->next;
+ free(stackptr);
+
+ return cellptr;
+}
+
+/*----------------------------------------------------------------------*/
+/* Routine that can be called to pre-install a definition into the */
+/* verilogdefs hash table. */
+/*----------------------------------------------------------------------*/
+
+void VerilogDefine(char *key, char *value)
+{
+ if (dictinit == FALSE) {
+ InitializeHashTable(&verilogdefs, TINYHASHSIZE);
+ dictinit = TRUE;
+ }
+ HashPtrInstall(key, strdup(value), &verilogdefs);
+}
+
+/*----------------------------------------------------------------------*/
+/* Function similar to strtok() for token parsing. The difference is */
+/* that it takes two sets of delimiters. The first is whitespace */
+/* delimiters, which separate tokens. The second is functional */
+/* delimiters, which separate tokens and have additional meaning, such */
+/* as parentheses, commas, semicolons, etc. The parser needs to know */
+/* when such tokens occur in the string, so they are returned as */
+/* individual tokens. */
+/* */
+/* Definition of delim2: String of single delimiter characters. The */
+/* special character "X" (which is never a delimiter in practice) is */
+/* used to separate single-character delimiters from two-character */
+/* delimiters (this presumably could be further extended as needed). */
+/* so ",;()" would be a valid delimiter set, but to include C-style */
+/* comments and verilog-style parameter lists, one would need */
+/* ",;()X/**///#(". */
+/*----------------------------------------------------------------------*/
+
+char *strdtok(char *pstring, char *delim1, char *delim2)
+{
+ static char *stoken = NULL;
+ static char *sstring = NULL;
+ char *s, *s2;
+ char first = FALSE;
+ int twofer;
+
+ if (pstring != NULL) {
+ /* Allocate enough memory to hold the string; tokens will be put here */
+ if (sstring != NULL) free(sstring);
+ sstring = (char *)malloc(strlen(pstring) + 1);
+ stoken = pstring;
+ first = TRUE;
+ }
+
+ /* Skip over "delim1" delimiters at the string beginning */
+ for (; *stoken; stoken++) {
+ for (s2 = delim1; *s2; s2++)
+ if (*stoken == *s2)
+ break;
+ if (*s2 == '\0') break;
+ }
+
+ if (*stoken == '\0') return NULL; /* Finished parsing */
+
+ /* "stoken" is now set. Now find the end of the current token */
+ /* Check string from position stoken. If a character in "delim2" is found, */
+ /* save the character in "lastdelim", null the byte at that position, and */
+ /* return the token. If a character in "delim1" is found, do the same but */
+ /* continue checking characters as long as there are contiguous "delim1" */
+ /* characters. If the series ends in a character from "delim2", then treat */
+ /* as for "delim2" above. If not, then set "lastdelim" to a null byte and */
+ /* return the token. */
+
+ s = stoken;
+
+ /* Special verilog rule: If a name begins with '\', then all characters */
+ /* are a valid part of the name until a space character is reached. The */
+ /* space character becomes part of the verilog name. The remainder of the */
+ /* name is parsed according to the rules of "delim2". */
+
+ if (*s == '\\') {
+ while (*s != '\0') {
+ if (*s == ' ') {
+ s++;
+ break;
+ }
+ s++;
+ }
+ }
+
+ for (; *s; s++) {
+ twofer = TRUE;
+ for (s2 = delim2; s2 && *s2; s2++) {
+ if (*s2 == 'X') {
+ twofer = FALSE;
+ continue;
+ }
+ if (twofer) {
+ if ((*s == *s2) && (*(s + 1) == *(s2 + 1))) {
+ if (s == stoken) {
+ strncpy(sstring, stoken, 2);
+ *(sstring + 2) = '\0';
+ stoken = s + 2;
+ }
+ else {
+ strncpy(sstring, stoken, (int)(s - stoken));
+ *(sstring + (s - stoken)) = '\0';
+ stoken = s;
+ }
+ return sstring;
+ }
+ s2++;
+ if (*s2 == '\0') break;
+ }
+ else if (*s == *s2) {
+ if (s == stoken) {
+ strncpy(sstring, stoken, 1);
+ *(sstring + 1) = '\0';
+ stoken = s + 1;
+ }
+ else {
+ strncpy(sstring, stoken, (int)(s - stoken));
+ *(sstring + (s - stoken)) = '\0';
+ stoken = s;
+ }
+ return sstring;
+ }
+ }
+ for (s2 = delim1; *s2; s2++) {
+ if (*s == *s2) {
+ strncpy(sstring, stoken, (int)(s - stoken));
+ *(sstring + (s - stoken)) = '\0';
+ stoken = s;
+ return sstring;
+ }
+ }
+ }
+ strcpy(sstring, stoken); /* Just copy to the end */
+ stoken = s;
+ return sstring;
+}
+
+/*----------------------------------------------------------------------*/
+/* File opening, closing, and stack handling */
+/* Return 0 on success, -1 on error. */
+/*----------------------------------------------------------------------*/
+
+int OpenParseFile(char *name)
+{
+ /* Push filestack */
+
+ FILE *locfile = NULL;
+ struct filestack *newfile;
+
+ locfile = fopen(name, "r");
+ vlinenum = 0;
+ /* reset the token scanner */
+ nexttok = NULL;
+
+ if (locfile != NULL) {
+ if (infile != NULL) {
+ newfile = (struct filestack *)malloc(sizeof(struct filestack));
+ newfile->file = infile;
+ newfile->next = OpenFiles;
+ OpenFiles = newfile;
+ }
+ infile = locfile;
+ }
+ return (locfile == NULL) ? -1 : 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+int EndParseFile(void)
+{
+ return feof(infile);
+}
+
+/*----------------------------------------------------------------------*/
+
+int CloseParseFile(void)
+{
+ struct filestack *lastfile;
+ int rval;
+ rval = fclose(infile);
+ infile = (FILE *)NULL;
+
+ /* Pop filestack if not empty */
+ lastfile = OpenFiles;
+ if (lastfile != NULL) {
+ OpenFiles = lastfile->next;
+ infile = lastfile->file;
+ free(lastfile);
+ }
+ return rval;
+}
+
+/*----------------------------------------------------------------------*/
+
+void InputParseError(FILE *f)
+{
+ char *ch;
+
+ fprintf(f, "line number %d = '", vlinenum);
+ for (ch = line; *ch != '\0'; ch++) {
+ if (isprint(*ch)) fprintf(f, "%c", *ch);
+ else if (*ch != '\n') fprintf(f, "<<%d>>", (int)(*ch));
+ }
+ fprintf(f, "'\n");
+}
+
+/*----------------------------------------------------------------------*/
+/* Tokenizer routines */
+/*----------------------------------------------------------------------*/
+
+#define WHITESPACE_DELIMITER " \t\n\r"
+
+/*----------------------------------------------------------------------*/
+/* TrimQuoted() --- */
+/* Remove spaces from inside single- or double-quoted strings. */
+/* Exclude bit values (e.g., "1'b0") */
+/*----------------------------------------------------------------------*/
+
+void TrimQuoted(char *line)
+{
+ char *qstart, *qend, *lptr;
+ int slen;
+ int changed;
+
+ /* Single-quoted entries */
+ changed = TRUE;
+ lptr = line;
+ while (changed)
+ {
+ changed = FALSE;
+ qstart = strchr(lptr, '\'');
+ if (qstart && (qstart > lptr)) {
+ if (isdigit(*(qstart - 1))) {
+ lptr = qstart + 1;
+ changed = TRUE;
+ continue;
+ }
+ }
+ if (qstart)
+ {
+ qend = strrchr(qstart + 1, '\'');
+ if (qend && (qend > qstart)) {
+ slen = strlen(lptr);
+ for (lptr = qstart + 1; lptr < qend; lptr++) {
+ if (*lptr == ' ') {
+ memmove(lptr, lptr + 1, slen);
+ qend--;
+ changed = TRUE;
+ }
+ }
+ lptr++;
+ }
+ }
+ }
+
+ /* Double-quoted entries */
+ changed = TRUE;
+ lptr = line;
+ while (changed)
+ {
+ changed = FALSE;
+ qstart = strchr(lptr, '\"');
+ if (qstart)
+ {
+ qend = strchr(qstart + 1, '\"');
+ if (qend && (qend > qstart)) {
+ slen = strlen(lptr);
+ for (lptr = qstart + 1; lptr < qend; lptr++) {
+ if (*lptr == ' ') {
+ memmove(lptr, lptr + 1, slen);
+ qend--;
+ changed = TRUE;
+ }
+ }
+ lptr++;
+ }
+ }
+ }
+}
+
+/*----------------------------------------------------------------------*/
+/* GetNextLineNoNewline() */
+/* */
+/* Fetch the next line, and grab the first token from the next line. */
+/* If there is no next token (next line is empty, and ends with a */
+/* newline), then place NULL in nexttok. */
+/* */
+/*----------------------------------------------------------------------*/
+
+int GetNextLineNoNewline(char *delimiter)
+{
+ char *newbuf;
+ int testc;
+ int nested = 0;
+ char *s, *t, *w, e, *kl;
+ int len, dlen, vlen, addin;
+ unsigned char found;
+
+ if (feof(infile)) return -1;
+
+ while (1) { /* May loop indefinitely in an `if[n]def conditional */
+
+ // This is more reliable than feof() ...
+ testc = getc(infile);
+ if (testc == -1) return -1;
+ ungetc(testc, infile);
+
+ if (linesize == 0) {
+ /* Allocate memory for line */
+ linesize = 500;
+ line = (char *)malloc(linesize);
+ linetok = (char *)malloc(linesize);
+ }
+ fgets(line, linesize, infile);
+ while (strlen(line) == linesize - 1) {
+ newbuf = (char *)malloc(linesize + 500);
+ strcpy(newbuf, line);
+ free(line);
+ line = newbuf;
+ fgets(line + linesize - 1, 501, infile);
+ linesize += 500;
+ free(linetok);
+ linetok = (char *)malloc(linesize * sizeof(char));
+ }
+
+ /* Check for substitutions. Make sure linetok is large enough */
+ /* to hold the entire line after substitutions. */
+
+ found = FALSE;
+ addin = 0;
+ for (s = line; *s != '\0'; s++) {
+ if (*s == '`') {
+ w = s + 1;
+ while (isalnum(*w)) w++;
+ e = *w;
+ *w = '\0';
+ kl = (char *)HashLookup(s + 1, &verilogdefs);
+ if (kl != NULL) {
+ dlen = strlen(s);
+ vlen = strlen(kl);
+ addin += vlen - dlen + 1;
+ found = TRUE;
+ }
+ *w = e;
+ }
+ }
+ if (found) {
+ len = strlen(line);
+ if (len + addin > linesize) {
+ while (len + addin > linesize) linesize += 500;
+ free(linetok);
+ linetok = (char *)malloc(linesize);
+ }
+ }
+
+ /* Make definition substitutions */
+
+ t = linetok;
+ for (s = line; *s != '\0'; s++) {
+ if (*s == '`') {
+ w = s + 1;
+ while (isalnum(*w)) w++;
+ e = *w;
+ *w = '\0';
+ kl = (char *)HashLookup(s + 1, &verilogdefs);
+ if (kl != NULL) {
+ strcpy(t, kl);
+ t += strlen(t);
+ s = w - 1;
+ }
+ else *t++ = *s;
+ *w = e;
+ }
+ else *t++ = *s;
+ }
+ *t = '\0';
+
+ TrimQuoted(linetok);
+ vlinenum++;
+
+ nexttok = strdtok(linetok, WHITESPACE_DELIMITER, delimiter);
+ if (nexttok == NULL) return 0;
+
+ /* Handle `ifdef, `ifndef, `elsif, `else, and `endif */
+
+ /* If currently skipping through a section, handle conditionals differently */
+ if (condstack) {
+ if (((condstack->invert == 0) && (condstack->kl == NULL))
+ || ((condstack->invert == 1) && (condstack->kl != NULL))) {
+ if (match(nexttok, "`ifdef") || match(nexttok, "`ifndef")) {
+ nested++;
+ continue;
+ }
+ else if (nested > 0) {
+ if (match(nexttok, "`endif")) nested--;
+ continue;
+ }
+ else if (nexttok[0] != '`') continue;
+ }
+ }
+
+ /* Handle conditionals (not being skipped over) */
+
+ if (match(nexttok, "`endif")) {
+ if (condstack == NULL) {
+ fprintf(stderr, "Error: `endif without corresponding `if[n]def\n");
+ }
+ else {
+ struct ifstack *iftop = condstack;
+ condstack = condstack->next;
+ free(iftop);
+ }
+ }
+
+ /* `if[n]def may be nested. */
+
+ else if (match(nexttok, "`ifdef") || match(nexttok, "`ifndef") ||
+ match(nexttok, "`elsif") || match(nexttok, "`else")) {
+
+ /* Every `ifdef or `ifndef increases condstack by 1 */
+ if (nexttok[1] == 'i') {
+ struct ifstack *newif = (struct ifstack *)malloc(sizeof(struct ifstack));
+ newif->next = condstack;
+ condstack = newif;
+ }
+ if (condstack == NULL) {
+ fprintf(stderr, "Error: %s without `if[n]def\n", nexttok);
+ break;
+ }
+ else {
+ if (match(nexttok, "`else")) {
+ /* Invert the sense of the if[n]def scope */
+ condstack->invert = (condstack->invert == 1) ? 0 : 1;
+ }
+ else if (match(nexttok, "`elsif")) {
+ nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter);
+ if (nexttok == NULL) {
+ fprintf(stderr, "Error: `elsif with no conditional.\n");
+ return 0;
+ }
+ /* Keep the same scope but redefine the parameter */
+ condstack->invert = 0;
+ condstack->kl = (char *)HashLookup(nexttok, &verilogdefs);
+ }
+ else {
+ condstack->invert = (nexttok[3] == 'n') ? 1 : 0;
+ nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter);
+ if (nexttok == NULL) {
+ fprintf(stderr, "Error: %s with no conditional.\n", nexttok);
+ return 0;
+ }
+ condstack->kl = (char *)HashLookup(nexttok, &verilogdefs);
+ }
+ }
+ }
+ else if (condstack) {
+ if (((condstack->invert == 0) && (condstack->kl == NULL))
+ || ((condstack->invert == 1) && (condstack->kl != NULL)))
+ continue;
+ else
+ break;
+ }
+ else
+ break;
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+/* Get the next line of input from file "infile", and find the first */
+/* valid token. */
+/*----------------------------------------------------------------------*/
+
+void GetNextLine(char *delimiter)
+{
+ do {
+ if (GetNextLineNoNewline(delimiter) == -1) return;
+ } while (nexttok == NULL);
+}
+
+/*----------------------------------------------------------------------*/
+/* skip to the end of the current line */
+/*----------------------------------------------------------------------*/
+
+void SkipNewLine(char *delimiter)
+{
+ while (nexttok != NULL)
+ nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter);
+}
+
+/*----------------------------------------------------------------------*/
+/* if nexttok is already NULL, force scanner to read new line */
+/*----------------------------------------------------------------------*/
+
+void SkipTok(char *delimiter)
+{
+ if (nexttok != NULL &&
+ (nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter)))
+ return;
+ GetNextLine(delimiter);
+}
+
+/*----------------------------------------------------------------------*/
+/* like SkipTok, but will not fetch a new line when the buffer is empty */
+/* must be preceeded by at least one call to SkipTok() */
+/*----------------------------------------------------------------------*/
+
+void SkipTokNoNewline(char *delimiter)
+{
+ nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter);
+}
+
+/*----------------------------------------------------------------------*/
+/* Skip to the next token, ignoring any C-style comments. */
+/*----------------------------------------------------------------------*/
+
+void SkipTokComments(char *delimiter)
+{
+ SkipTok(delimiter);
+ while (nexttok) {
+ if (!strcmp(nexttok, "//")) {
+ SkipNewLine(delimiter);
+ SkipTok(delimiter);
+ }
+ else if (!strcmp(nexttok, "/*")) {
+ while (nexttok && strcmp(nexttok, "*/"))
+ SkipTok(delimiter);
+ if (nexttok) SkipTok(delimiter);
+ }
+ else if (!strcmp(nexttok, "(*")) {
+ while (nexttok && strcmp(nexttok, "*)"))
+ SkipTok(delimiter);
+ if (nexttok) SkipTok(delimiter);
+ }
+ else break;
+ }
+}
+
+/*----------------------------------------------------------------------*/
+/* Free a bus structure in the hash table during cleanup */
+/*----------------------------------------------------------------------*/
+
+int freenet (struct hashlist *p)
+{
+ struct netrec *wb;
+
+ wb = (struct netrec *)(p->ptr);
+ free(wb);
+ return 1;
+}
+
+/*----------------------------------------------------------------------*/
+/* Create a new net (or bus) structure */
+/*----------------------------------------------------------------------*/
+
+struct netrec *NewNet()
+{
+ struct netrec *wb;
+
+ wb = (struct netrec *)calloc(1, sizeof(struct netrec));
+ if (wb == NULL) fprintf(stderr, "NewNet: Core allocation error\n");
+ return (wb);
+}
+
+/*----------------------------------------------------------------------*/
+/* BusHashLookup -- */
+/* Run HashLookup() on a string, first removing any bus delimiters. */
+/*----------------------------------------------------------------------*/
+
+void *BusHashLookup(char *s, struct hashtable *table)
+{
+ void *rval;
+ char *dptr = NULL;
+ char *sptr = s;
+
+ if (*sptr == '\\') {
+ sptr = strchr(s, ' ');
+ if (sptr == NULL) sptr = s;
+ }
+ if ((dptr = strchr(sptr, '[')) != NULL) *dptr = '\0';
+
+ rval = HashLookup(s, table);
+ if (dptr) *dptr = '[';
+ return rval;
+}
+
+/*----------------------------------------------------------------------*/
+/* BusHashPtrInstall -- */
+/* Run HashPtrInstall() on a string, first removing any bus delimiters. */
+/*----------------------------------------------------------------------*/
+
+struct hashlist *BusHashPtrInstall(char *name, void *ptr,
+ struct hashtable *table)
+{
+ struct hashlist *rval;
+ char *dptr = NULL;
+ char *sptr = name;
+
+ if (*sptr == '\\') {
+ sptr = strchr(name, ' ');
+ if (sptr == NULL) sptr = name;
+ }
+ if ((dptr = strchr(sptr, '[')) != NULL) *dptr = '\0';
+
+ rval = HashPtrInstall(name, ptr, table);
+ if (dptr) *dptr = '[';
+ return rval;
+}
+
+/*------------------------------------------------------------------------------*/
+/* Get bus indexes from the notation name[a:b]. If there is only "name" */
+/* then look up the name in the bus hash list and return the index bounds. */
+/* Return 0 on success, 1 on syntax error, and -1 if signal is not a bus. */
+/* */
+/* Note that this routine relies on the delimiter characters including */
+/* "[", ":", and "]" when calling NextTok. */
+/*------------------------------------------------------------------------------*/
+
+int GetBusTok(struct netrec *wb, struct hashtable *nets)
+{
+ int result, start, end;
+ char *kl;
+
+ if (wb == NULL) return 0;
+ else {
+ wb->start = -1;
+ wb->end = -1;
+ }
+
+ if (!strcmp(nexttok, "[")) {
+ SkipTokComments(VLOG_DELIMITERS);
+
+ // Check for parameter names and substitute values if found.
+ result = sscanf(nexttok, "%d", &start);
+ if (result != 1) {
+ char *aptr = NULL;
+ char addin;
+
+ // Check for "+/-(n)" at the end of a parameter name
+ aptr = strrchr(nexttok, '+');
+ if (aptr == NULL) aptr = strrchr(nexttok, '-');
+ if (aptr != NULL) {
+ addin = *aptr;
+ *aptr = '\0';
+ }
+
+ // Is name in the parameter list?
+ kl = (char *)HashLookup(nexttok, &verilogparams);
+ if (kl == NULL) {
+ fprintf(stdout, "Array value %s is not a number or a "
+ "parameter (line %d).\n", nexttok, vlinenum);
+ return 1;
+ }
+ else {
+ result = sscanf(kl, "%d", &start);
+ if (result != 1) {
+ fprintf(stdout, "Parameter %s has value %s that cannot be parsed"
+ " as an integer (line %d).\n", nexttok, kl, vlinenum);
+ return 1;
+ }
+ }
+ if (aptr != NULL) {
+ int addval;
+ *aptr = addin;
+ if (sscanf(aptr + 1, "%d", &addval) != 1) {
+ fprintf(stdout, "Unable to parse parameter increment \"%s\" "
+ "(line %d).\n", aptr, vlinenum);
+ return 1;
+ }
+ start += (addin == '+') ? addval : -addval;
+ }
+ }
+
+ SkipTokComments(VLOG_DELIMITERS);
+ if (!strcmp(nexttok, "]")) {
+ result = 1;
+ end = start; // Single bit
+ }
+ else if (strcmp(nexttok, ":")) {
+ fprintf(stdout, "Badly formed array notation: Expected colon, "
+ "found %s (line %d)\n", nexttok, vlinenum);
+ return 1;
+ }
+ else {
+ SkipTokComments(VLOG_DELIMITERS);
+
+ // Check for parameter names and substitute values if found.
+ result = sscanf(nexttok, "%d", &end);
+ if (result != 1) {
+ char *aptr = NULL;
+ char addin;
+
+ // Check for "+/-(n)" at the end of a parameter name
+ aptr = strrchr(nexttok, '+');
+ if (aptr == NULL) aptr = strrchr(nexttok, '-');
+ if (aptr != NULL) {
+ addin = *aptr;
+ *aptr = '\0';
+ }
+
+ // Is name in the parameter list?
+ kl = (char *)HashLookup(nexttok, &verilogparams);
+ if (kl == NULL) {
+ fprintf(stdout, "Array value %s is not a number or a "
+ "parameter (line %d).\n", nexttok, vlinenum);
+ return 1;
+ }
+ else {
+ result = sscanf(kl, "%d", &end);
+ if (result != 1) {
+ fprintf(stdout, "Parameter %s has value %s that cannot"
+ " be parsed as an integer (line %d).\n",
+ nexttok, kl, vlinenum);
+ return 1;
+ }
+ }
+ if (aptr != NULL) {
+ int addval;
+ *aptr = addin;
+ if (sscanf(aptr + 1, "%d", &addval) != 1) {
+ fprintf(stdout, "Unable to parse parameter increment \"%s\" "
+ "(line %d).\n", aptr, vlinenum);
+ return 1;
+ }
+ end += (addin == '+') ? addval : -addval;
+ }
+ }
+ }
+ wb->start = start;
+ wb->end = end;
+
+ while (strcmp(nexttok, "]")) {
+ SkipTokComments(VLOG_DELIMITERS);
+ if (nexttok == NULL) {
+ fprintf(stdout, "End of file reached while reading array bounds.\n");
+ return 1;
+ }
+ else if (!strcmp(nexttok, ";")) {
+ // Better than reading to end-of-file, give up on end-of-statement
+ fprintf(stdout, "End of statement reached while reading "
+ "array bounds (line %d).\n", vlinenum);
+ return 1;
+ }
+ }
+ /* Move token forward to bus name */
+ SkipTokComments(VLOG_DELIMITERS);
+ }
+ else if (nets) {
+ struct netrec *hbus;
+ hbus = (struct netrec *)BusHashLookup(nexttok, nets);
+ if (hbus != NULL) {
+ wb->start = hbus->start;
+ wb->end = hbus->end;
+ }
+ else
+ return -1;
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+/* GetBus() is similar to GetBusTok() (see above), but it parses from */
+/* a string instead of the input tokenizer. */
+/*----------------------------------------------------------------------*/
+
+int GetBus(char *astr, struct netrec *wb, struct hashtable *nets)
+{
+ char *colonptr, *brackstart, *brackend, *sstr;
+ int result, start, end;
+
+ if (wb == NULL) return 0;
+ else {
+ wb->start = -1;
+ wb->end = -1;
+ }
+ sstr = astr;
+
+ // Skip to the end of verilog names bounded by '\' and ' '
+ if (*sstr == '\\')
+ while (*sstr && *sstr != ' ') sstr++;
+
+ brackstart = strchr(sstr, '[');
+ if (brackstart != NULL) {
+ brackend = strchr(sstr, ']');
+ if (brackend == NULL) {
+ fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr,
+ vlinenum);
+ return 1;
+ }
+ *brackend = '\0';
+ colonptr = strchr(sstr, ':');
+ if (colonptr) *colonptr = '\0';
+ result = sscanf(brackstart + 1, "%d", &start);
+ if (colonptr) *colonptr = ':';
+ if (result != 1) {
+ fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr,
+ vlinenum);
+ *brackend = ']';
+ return 1;
+ }
+ if (colonptr)
+ result = sscanf(colonptr + 1, "%d", &end);
+ else {
+ result = 1;
+ end = start; // Single bit
+ }
+ *brackend = ']';
+ if (result != 1) {
+ fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr,
+ vlinenum);
+ return 1;
+ }
+ wb->start = start;
+ wb->end = end;
+ }
+ else if (nets) {
+ struct netrec *hbus;
+ hbus = (struct netrec *)BusHashLookup(astr, nets);
+ if (hbus != NULL) {
+ wb->start = hbus->start;
+ wb->end = hbus->end;
+ }
+ else
+ return -1;
+ }
+ return 0;
+}
+
+/*------------------------------------------------------*/
+/* Add net to cell database */
+/*------------------------------------------------------*/
+
+struct netrec *Net(struct cellrec *cell, char *netname)
+{
+ struct netrec *newnet;
+
+ newnet = NewNet();
+ newnet->start = -1;
+ newnet->end = -1;
+
+ /* Install net in net hash */
+ BusHashPtrInstall(netname, newnet, &cell->nets);
+
+ return newnet;
+}
+
+/*------------------------------------------------------*/
+/* Add port to instance record */
+/*------------------------------------------------------*/
+
+struct portrec *InstPort(struct instance *inst, char *portname, char *netname)
+{
+ struct portrec *portsrch, *newport;
+
+ newport = (struct portrec *)malloc(sizeof(struct portrec));
+ newport->name = strdup(portname);
+ newport->direction = PORT_NONE;
+ if (netname)
+ newport->net = strdup(netname);
+ else
+ newport->net = NULL;
+ newport->next = NULL;
+
+ /* Go to end of the port list */
+ if (inst->portlist == NULL) {
+ inst->portlist = newport;
+ }
+ else {
+ for (portsrch = inst->portlist; portsrch->next; portsrch = portsrch->next);
+ portsrch->next = newport;
+ }
+ return newport;
+}
+
+/*------------------------------------------------------*/
+/* Add port to cell database */
+/*------------------------------------------------------*/
+
+void Port(struct cellrec *cell, char *portname, struct netrec *net, int port_type)
+{
+ struct portrec *portsrch, *newport;
+ struct netrec *newnet;
+
+ newport = (struct portrec *)malloc(sizeof(struct portrec));
+ if (portname)
+ newport->name = strdup(portname);
+ else
+ newport->name = NULL;
+ newport->direction = port_type;
+ newport->net = NULL;
+ newport->next = NULL;
+
+ /* Go to end of the port list */
+ if (cell->portlist == NULL) {
+ cell->portlist = newport;
+ }
+ else {
+ for (portsrch = cell->portlist; portsrch->next; portsrch = portsrch->next);
+ portsrch->next = newport;
+ }
+
+ /* Register the port name as a net in the cell */
+ if (portname)
+ {
+ newnet = Net(cell, portname);
+ if (net) {
+ newnet->start = net->start;
+ newnet->end = net->end;
+ }
+ }
+}
+
+/*------------------------------------------------------*/
+/* Create a new cell */
+/*------------------------------------------------------*/
+
+struct cellrec *Cell(char *cellname)
+{
+ struct cellrec *new_cell;
+
+ new_cell = (struct cellrec *)malloc(sizeof(struct cellrec));
+ new_cell->name = strdup(cellname);
+ new_cell->portlist = NULL;
+ new_cell->instlist = NULL;
+ new_cell->lastinst = NULL;
+
+ InitializeHashTable(&new_cell->nets, LARGEHASHSIZE);
+ InitializeHashTable(&new_cell->propdict, TINYHASHSIZE);
+
+ return new_cell;
+}
+
+/*------------------------------------------------------*/
+/* Add instance to cell database */
+/*------------------------------------------------------*/
+
+struct instance *Instance(struct cellrec *cell, char *cellname, int prepend)
+{
+ struct instance *newinst, *instsrch;
+
+ /* Create new instance record */
+ newinst = (struct instance *)malloc(sizeof(struct instance));
+
+ newinst->instname = NULL;
+ newinst->cellname = strdup(cellname);
+ newinst->portlist = NULL;
+ newinst->next = NULL;
+
+ InitializeHashTable(&newinst->propdict, TINYHASHSIZE);
+
+ if (cell->instlist == NULL) {
+ cell->instlist = newinst;
+ }
+ else {
+ if (prepend == TRUE) {
+ newinst->next = cell->instlist;
+ cell->instlist = newinst;
+ }
+ else {
+ instsrch = (cell->lastinst != NULL) ?
+ cell->lastinst : cell->instlist;
+
+ /* Go to end of the instance list */
+ for (; instsrch->next; instsrch = instsrch->next);
+
+ cell->lastinst = instsrch;
+ instsrch->next = newinst;
+ }
+ }
+ return newinst;
+}
+
+struct instance *AppendInstance(struct cellrec *cell, char *cellname)
+{
+ return Instance(cell, cellname, FALSE);
+}
+
+struct instance *PrependInstance(struct cellrec *cell, char *cellname)
+{
+ return Instance(cell, cellname, TRUE);
+}
+
+/*------------------------------------------------------*/
+/* Read a verilog structural netlist */
+/*------------------------------------------------------*/
+
+void ReadVerilogFile(char *fname, struct cellstack **CellStackPtr,
+ int blackbox)
+{
+ int i;
+ int warnings = 0, hasports, inlined_decls = 0, localcount = 1;
+ int port_type = PORT_NONE;
+ char in_module, in_param;
+ char *eqptr;
+ char pkey[256];
+
+ struct cellrec *top = NULL;
+
+ in_module = (char)0;
+ in_param = (char)0;
+
+ while (!EndParseFile()) {
+
+ SkipTokComments(VLOG_DELIMITERS); /* get the next token */
+
+ /* Diagnostic */
+ /* if (nexttok) fprintf(stdout, "Token is \"%s\"\n", nexttok); */
+
+ if ((EndParseFile()) && (nexttok == NULL)) break;
+ else if (nexttok == NULL)
+ break;
+
+ /* Ignore end-of-statement markers */
+ else if (!strcmp(nexttok, ";"))
+ continue;
+
+ /* Ignore primitive definitions */
+ else if (!strcmp(nexttok, "primitive")) {
+ while (1) {
+ SkipNewLine(VLOG_DELIMITERS);
+ SkipTokComments(VLOG_DELIMITERS);
+ if (EndParseFile()) break;
+ if (!strcmp(nexttok, "endprimitive")) {
+ in_module = 0;
+ break;
+ }
+ }
+ }
+
+ else if (!strcmp(nexttok, "module")) {
+ struct netrec wb;
+
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ if (nexttok == NULL) {
+ fprintf(stderr, "Badly formed \"module\" line (line %d)\n", vlinenum);
+ goto skip_endmodule;
+ }
+
+ if (in_module == (char)1) {
+ fprintf(stderr, "Missing \"endmodule\" statement on "
+ "subcircuit (line %d).\n", vlinenum);
+ InputParseError(stderr);
+ }
+ in_module = (char)1;
+ hasports = (char)0;
+ inlined_decls = (char)0;
+
+ /* If there is an existing module, then push it */
+ if (top != NULL) PushStack(top, CellStackPtr);
+
+ /* Create new cell */
+ top = Cell(nexttok);
+
+ /* Need to support both types of I/O lists: Those */
+ /* that declare names only in the module list and */
+ /* follow with input/output and vector size */
+ /* declarations as individual statements in the */
+ /* module definition, and those which declare */
+ /* everything inside the pin list. */
+
+ SkipTokComments(VLOG_DELIMITERS);
+
+ // Check for parameters within #( ... )
+
+ if (!strcmp(nexttok, "#(")) {
+ SkipTokComments(VLOG_DELIMITERS);
+ in_param = (char)1;
+ }
+ else if (!strcmp(nexttok, "(")) {
+ SkipTokComments(VLOG_DELIMITERS);
+ }
+
+ wb.start = wb.end = -1;
+ while ((nexttok != NULL) && strcmp(nexttok, ";")) {
+ if (in_param) {
+ if (!strcmp(nexttok, ")")) {
+ in_param = (char)0;
+ SkipTokComments(VLOG_DELIMITERS);
+ if (strcmp(nexttok, "(")) {
+ fprintf(stderr, "Badly formed module block parameter"
+ " list (line %d).\n", vlinenum);
+ goto skip_endmodule;
+ }
+ }
+ else if (!strcmp(nexttok, "=")) {
+
+ // The parameter value is the next token.
+ SkipTokComments(VLOG_DELIMITERS); /* get the next token */
+ HashPtrInstall(pkey, strdup(nexttok), &top->propdict);
+ }
+ else {
+ /* Assume this is a keyword and save it */
+ strcpy(pkey, nexttok);
+ }
+ }
+ else if (strcmp(nexttok, ",")) {
+ if (!strcmp(nexttok, ")")) break;
+ // Ignore input, output, and inout keywords, and handle buses.
+
+ if (inlined_decls == (char)0) {
+ if (!strcmp(nexttok, "input") || !strcmp(nexttok, "output")
+ || !strcmp(nexttok, "inout"))
+ inlined_decls = (char)1;
+ }
+
+ if (inlined_decls == (char)1) {
+ if (!strcmp(nexttok, "input"))
+ port_type = PORT_INPUT;
+ else if (!strcmp(nexttok, "output"))
+ port_type = PORT_OUTPUT;
+ else if (!strcmp(nexttok, "inout"))
+ port_type = PORT_INOUT;
+ else if (strcmp(nexttok, "real") && strcmp(nexttok, "logic")
+ && strcmp(nexttok, "wire")
+ && strcmp(nexttok, "integer")) {
+ if (!strcmp(nexttok, "[")) {
+ if (GetBusTok(&wb, &top->nets) != 0) {
+ // Didn't parse as a bus, so wing it
+ Port(top, nexttok, NULL, port_type);
+ }
+ else
+ Port(top, nexttok, &wb, port_type);
+ }
+ else
+ Port(top, nexttok, NULL, port_type);
+
+ hasports = 1;
+ }
+ }
+ }
+ SkipTokComments(VLOG_DELIMITERS);
+ if (nexttok == NULL) break;
+ }
+ if (inlined_decls == 1) {
+ if (hasports == 0)
+ // If the cell defines no ports, then create a proxy
+ Port(top, (char *)NULL, NULL, PORT_NONE);
+
+ /* In the blackbox case, don't read the cell contents */
+ if (blackbox) goto skip_endmodule;
+ }
+ }
+ else if (!strcmp(nexttok, "input") || !strcmp(nexttok, "output")
+ || !strcmp(nexttok, "inout")) {
+ struct netrec wb;
+
+ if (!strcmp(nexttok, "input")) port_type = PORT_INPUT;
+ else if (!strcmp(nexttok, "output")) port_type = PORT_OUTPUT;
+ else if (!strcmp(nexttok, "inout")) port_type = PORT_INOUT;
+ else port_type = PORT_NONE;
+
+ // Parsing of ports as statements not in the module pin list.
+ wb.start = wb.end = -1;
+ while (1) {
+ SkipTokComments(VLOG_DELIMITERS);
+ if (EndParseFile()) break;
+
+ if (!strcmp(nexttok, ";")) {
+ // End of statement
+ break;
+ }
+ else if (!strcmp(nexttok, "[")) {
+ if (GetBusTok(&wb, &top->nets) != 0) {
+ // Didn't parse as a bus, so wing it
+ Port(top, nexttok, NULL, port_type);
+ }
+ else
+ Port(top, nexttok, &wb, port_type);
+ }
+ else if (strcmp(nexttok, ",")) {
+ /* Comma-separated list; use same bus limits */
+ Port(top, nexttok, &wb, port_type);
+ }
+ hasports = 1;
+ }
+ }
+ else if (!strcmp(nexttok, "endmodule")) {
+
+ if (in_module == (char)0) {
+ fprintf(stderr, "\"endmodule\" occurred outside of a "
+ "module (line %d)!\n", vlinenum);
+ InputParseError(stderr);
+ }
+ in_module = (char)0;
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ else if (!strcmp(nexttok, "`include")) {
+ char *iname, *iptr, *quotptr, *pathend, *userpath = NULL;
+
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ if (nexttok == NULL) continue; /* Ignore if no filename */
+
+ // Any file included in another Verilog file needs to be
+ // interpreted relative to the path of the parent Verilog file,
+ // unless it's an absolute pathname.
+
+ pathend = strrchr(fname, '/');
+ iptr = nexttok;
+ while (*iptr == '\'' || *iptr == '\"') iptr++;
+ if ((pathend != NULL) && (*iptr != '/') && (*iptr != '~')) {
+ *pathend = '\0';
+ iname = (char *)malloc(strlen(fname) + strlen(iptr) + 2);
+ sprintf(iname, "%s/%s", fname, iptr);
+ *pathend = '/';
+ }
+ else if ((*iptr == '~') && (*(iptr + 1) == '/')) {
+ /* For ~/<path>, substitute tilde from $HOME */
+ userpath = getenv("HOME");
+ iname = (char *)malloc(strlen(userpath) + strlen(iptr));
+ sprintf(iname, "%s%s", userpath, iptr + 1);
+ }
+ else if (*iptr == '~') {
+ /* For ~<user>/<path>, substitute tilde from getpwnam() */
+ struct passwd *passwd;
+ char *pathstart;
+ pathstart = strchr(iptr, '/');
+ if (pathstart) *pathstart = '\0';
+ passwd = getpwnam(iptr + 1);
+ if (passwd != NULL) {
+ userpath = passwd->pw_dir;
+ if (pathstart) {
+ *pathstart = '/';
+ iname = (char *)malloc(strlen(userpath) + strlen(pathstart) + 1);
+ sprintf(iname, "%s%s", userpath, pathstart);
+ }
+ else {
+ /* Almost certainly an error, but make the substitution anyway */
+ iname = strdup(userpath);
+ }
+ }
+ else {
+ /* Probably an error, but copy the filename verbatim */
+ iname = strdup(iptr);
+ }
+ }
+ else
+ iname = strdup(iptr);
+
+ // Eliminate any single or double quotes around the filename
+ iptr = iname;
+ quotptr = iptr;
+ while (*quotptr != '\'' && *quotptr != '\"' &&
+ *quotptr != '\0' && *quotptr != '\n') quotptr++;
+ if (*quotptr == '\'' || *quotptr == '\"') *quotptr = '\0';
+
+ IncludeVerilog(iptr, CellStackPtr, blackbox);
+ free(iname);
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ else if (!strcmp(nexttok, "`define")) {
+ char *key;
+
+ /* Parse for definitions used in expressions. Save */
+ /* definitions in the "verilogdefs" hash table. */
+
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
+
+ key = strdup(nexttok);
+
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ if ((nexttok == NULL) || (nexttok[0] == '\0'))
+ /* Let "`define X" be equivalent to "`define X 1". */
+ HashPtrInstall(key, strdup("1"), &verilogdefs);
+ else
+ HashPtrInstall(key, strdup(nexttok), &verilogdefs);
+ }
+ else if (!strcmp(nexttok, "parameter") || !strcmp(nexttok, "localparam")) {
+ char *paramkey = NULL;
+ char *paramval = NULL;
+
+ // Pick up key = value pairs and store in current cell. Look only
+ // at the keyword before "=". Then set the definition as everything
+ // remaining in the line, excluding comments, until the end-of-statement
+
+ while (nexttok != NULL)
+ {
+ /* Parse for parameters used in expressions. Save */
+ /* parameters in the "verilogparams" hash table. */
+
+ SkipTok(VLOG_DELIMITERS);
+ if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
+ if (!strcmp(nexttok, "=")) {
+ /* Pick up remainder of statement */
+ while (nexttok != NULL) {
+ SkipTokNoNewline("X///**/X;,");
+ if (nexttok == NULL) break;
+ if (!strcmp(nexttok, ";") || !strcmp(nexttok, ",")) break;
+ if (paramval == NULL) paramval = strdup(nexttok);
+ else {
+ char *paramlast;
+ /* Append nexttok to paramval */
+ paramlast = paramval;
+ paramval = (char *)malloc(strlen(paramlast) +
+ strlen(nexttok) + 2);
+ sprintf(paramval, "%s %s", paramlast, nexttok);
+ free(paramlast);
+ }
+ }
+ HashPtrInstall(paramkey, paramval, &verilogparams);
+ free(paramval);
+ paramval = NULL;
+ if ((nexttok == NULL) || !strcmp(nexttok, ";")) break;
+ }
+ else {
+ if (paramkey != NULL) free(paramkey);
+ paramkey = strdup(nexttok);
+ }
+ }
+ if (paramval != NULL) free(paramval);
+ if (paramkey != NULL) free(paramkey);
+ }
+ else if (!strcmp(nexttok, "real") || !strcmp(nexttok, "integer")) {
+ fprintf(stdout, "Ignoring '%s' in module '%s' (line %d)\n",
+ nexttok, top->name, vlinenum);
+ while (strcmp(nexttok, ";")) SkipTok("X///**/X,;");
+ continue;
+ }
+ else if (!strcmp(nexttok, "wire") ||
+ !strcmp(nexttok, "assign")) { /* wire = node */
+ struct netrec wb, *nb;
+ char *eptr, *wirename;
+ char is_assignment = FALSE;
+ char is_lhs_bundle = FALSE, is_rhs_bundle = FALSE;
+ char is_wire = (strcmp(nexttok, "wire")) ? FALSE : TRUE;
+
+ // Several allowed uses of "assign":
+ // "assign a = b" joins two nets.
+ // "assign a = {b, c, ...}" creates a signal bundle from components.
+ // "assign {a, b ...} = {c, d, ...}" creates a bundle from another bundle.
+ // "assign" using any boolean arithmetic is not structural verilog.
+
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ if (!strcmp(nexttok, "real"))
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ else if (!strcmp(nexttok, "logic"))
+ SkipTokNoNewline(VLOG_DELIMITERS);
+
+ while (nexttok != NULL) {
+ if (!strcmp(nexttok, "=")) {
+ is_assignment = TRUE;
+ }
+ else if (!strcmp(nexttok, "{")) {
+ /* To be done: allow nested bundles */
+ if (is_assignment == FALSE) is_lhs_bundle = TRUE;
+ else is_rhs_bundle = TRUE;
+ }
+ else if (!strcmp(nexttok, "}")) {
+ if (is_assignment == FALSE) is_lhs_bundle = FALSE;
+ else is_rhs_bundle = FALSE;
+ }
+ else if (!strcmp(nexttok, ",")) {
+ /* Do nothing; moving to the next wire or bundle component */
+ }
+ else if (GetBusTok(&wb, &top->nets) == 0) {
+ if (is_wire) {
+ /* Handle bus notation (wires only) */
+ if ((nb = BusHashLookup(nexttok, &top->nets)) == NULL)
+ nb = Net(top, nexttok);
+ if (nb->start == -1) {
+ nb->start = wb.start;
+ nb->end = wb.end;
+ }
+ else {
+ if (nb->start < wb.start) nb->start = wb.start;
+ if (nb->end > wb.end) nb->end = wb.end;
+ }
+ }
+ else {
+ /* "assign" to a bus subnet. This will be ignored */
+ /* until this tool handles bus joining. If the */
+ /* assignment is made on an undeclared wire, then */
+ /* adjust the wire bounds. */
+ if (nb && nb->start == -1) {
+ nb->start = wb.start;
+ nb->end = wb.end;
+ }
+ }
+ }
+ else {
+ if (is_assignment) {
+ char *qptr;
+ int idum;
+
+ if (BusHashLookup(nexttok, &top->nets) != NULL) {
+ /* Join nets */
+ /* (WIP) */
+ }
+ else if ((qptr = strchr(nexttok, '\'')) != NULL) {
+ *qptr = '\0';
+ if (sscanf(nexttok, "%d", &idum) == 1) {
+ if ((strchr(qptr + 1, 'x') == NULL) &&
+ (strchr(qptr + 1, 'X') == NULL) &&
+ (strchr(qptr + 1, 'z') == NULL) &&
+ (strchr(qptr + 1, 'Z') == NULL)) {
+ // Power/Ground denoted by, e.g., "vdd = 1'b1".
+ // Only need to record the net, no further action
+ // needed.
+ }
+ else {
+ fprintf(stdout, "Assignment is not a net "
+ "(line %d).\n", vlinenum);
+ fprintf(stdout, "Module '%s' is not structural "
+ "verilog, making black-box.\n", top->name);
+ goto skip_endmodule;
+ }
+ }
+ *qptr = '\'';
+ }
+ else {
+ fprintf(stdout, "Assignment is not a net (line %d).\n",
+ vlinenum);
+ fprintf(stdout, "Module '%s' is not structural verilog,"
+ " making black-box.\n", top->name);
+ goto skip_endmodule;
+ }
+ is_assignment = FALSE;
+ }
+ else if (BusHashLookup(nexttok, &top->nets) == NULL)
+ Net(top, nexttok);
+ }
+ do {
+ SkipTokNoNewline(VLOG_DELIMITERS);
+ } while (nexttok && !strcmp(nexttok, ";"));
+ }
+ }
+ else if (!strcmp(nexttok, "endmodule")) {
+ // No action---new module is started with next 'module' statement,
+ // if any.
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ else if (nexttok[0] == '`') {
+ // Ignore any other directive starting with a backtick
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ else if (!strcmp(nexttok, "reg") || !strcmp(nexttok, "always") ||
+ !strcmp(nexttok, "specify") || !strcmp(nexttok, "initial")) {
+ fprintf(stdout, "Behavioral keyword '%s' found in source.\n", nexttok);
+ fprintf(stdout, "Module '%s' is not structural verilog, making "
+ "black-box.\n", top->name);
+ goto skip_endmodule;
+ }
+ else { /* module instances */
+ char ignore;
+ int itype, arraymax, arraymin;
+ struct instance *thisinst;
+
+ thisinst = AppendInstance(top, nexttok);
+
+ SkipTokComments(VLOG_DELIMITERS);
+
+nextinst:
+ ignore = FALSE;
+
+ // Next token must be '#(' (parameters) or an instance name
+
+ if (!strcmp(nexttok, "#(")) {
+
+ // Read the parameter list
+ SkipTokComments(VLOG_DELIMITERS);
+
+ while (nexttok != NULL) {
+ char *paramname;
+
+ if (!strcmp(nexttok, ")")) {
+ SkipTokComments(VLOG_DELIMITERS);
+ break;
+ }
+ else if (!strcmp(nexttok, ",")) {
+ SkipTokComments(VLOG_DELIMITERS);
+ continue;
+ }
+
+ // We need to look for parameters of the type ".name(value)"
+
+ else if (nexttok[0] == '.') {
+ paramname = strdup(nexttok + 1);
+ SkipTokComments(VLOG_DELIMITERS);
+ if (strcmp(nexttok, "(")) {
+ fprintf(stdout, "Error: Expecting parameter value, "
+ "got %s (line %d).\n", nexttok, vlinenum);
+ }
+ SkipTokComments(VLOG_DELIMITERS);
+ if (!strcmp(nexttok, ")")) {
+ fprintf(stdout, "Error: Parameter with no value found"
+ " (line %d).\n", vlinenum);
+ }
+ else {
+ HashPtrInstall(paramname, strdup(nexttok),
+ &thisinst->propdict);
+ SkipTokComments(VLOG_DELIMITERS);
+ if (strcmp(nexttok, ")")) {
+ fprintf(stdout, "Error: Expecting end of parameter "
+ "value, got %s (line %d).\n", nexttok,
+ vlinenum);
+ }
+ }
+ free(paramname);
+ }
+ SkipTokComments(VLOG_DELIMITERS);
+ }
+ if (!nexttok) {
+ fprintf(stdout, "Error: Still reading module, but got "
+ "end-of-file.\n");
+ goto skip_endmodule;
+ }
+ }
+
+ thisinst->instname = strdup(nexttok);
+
+ /* fprintf(stdout, "Diagnostic: new instance is %s\n", */
+ /* thisinst->instname); */
+ SkipTokComments(VLOG_DELIMITERS);
+
+ thisinst->arraystart = thisinst->arrayend = -1;
+ if (!strcmp(nexttok, "[")) {
+ // Handle instance array notation.
+ struct netrec wb;
+ if (GetBusTok(&wb, NULL) == 0) {
+ thisinst->arraystart = wb.start;
+ thisinst->arrayend = wb.end;
+ }
+ }
+
+ if (!strcmp(nexttok, "(")) {
+ char savetok = (char)0;
+ struct portrec *new_port;
+ struct netrec *nb, wb;
+ char in_line = FALSE, *in_line_net = NULL;
+ char *ncomp, *nptr;
+
+ // Read the pin list
+ while (nexttok != NULL) {
+ SkipTokComments(VLOG_DELIMITERS);
+ // NOTE: Deal with `ifdef et al. properly. Ignoring for now.
+ while (nexttok[0] == '`') {
+ SkipNewLine(VLOG_DELIMITERS);
+ SkipTokComments(VLOG_DELIMITERS);
+ }
+ if (!strcmp(nexttok, ")")) break;
+ else if (!strcmp(nexttok, ",")) continue;
+
+ // We need to look for pins of the type ".name(value)"
+
+ if (nexttok[0] != '.') {
+ fprintf(stdout, "Ignoring subcircuit with no pin names "
+ "at \"%s\" (line %d)\n",
+ nexttok, vlinenum);
+ while (nexttok != NULL) {
+ SkipTokComments(VLOG_DELIMITERS);
+ if (match(nexttok, ";")) break;
+ }
+ ignore = TRUE;
+ break;
+ }
+ else {
+ new_port = InstPort(thisinst, strdup(nexttok + 1), NULL);
+ SkipTokComments(VLOG_DELIMITERS);
+ if (strcmp(nexttok, "(")) {
+ fprintf(stdout, "Badly formed subcircuit pin line "
+ "at \"%s\" (line %d)\n", nexttok, vlinenum);
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
+ if (!strcmp(nexttok, ")")) {
+ char localnet[100];
+ // Empty parens, so create a new local node
+ savetok = (char)1;
+ sprintf(localnet, "_noconnect_%d_", localcount++);
+ new_port->net = strdup(localnet);
+ }
+ else {
+
+ if (!strcmp(nexttok, "{")) {
+ char *in_line_net = (char *)malloc(1);
+ *in_line_net = '\0';
+ /* In-line array---Read to "}" */
+ while (nexttok) {
+ in_line_net = (char *)realloc(in_line_net,
+ strlen(in_line_net) +
+ strlen(nexttok) + 1);
+ strcat(in_line_net, nexttok);
+ if (!strcmp(nexttok, "}")) break;
+ SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
+ }
+ if (!nexttok) {
+ fprintf(stderr, "Unterminated net in pin %s "
+ "(line %d)\n", in_line_net,
+ vlinenum);
+ }
+ new_port->net = in_line_net;
+ }
+ else
+ new_port->net = strdup(nexttok);
+
+ /* Read array information along with name; */
+ /* will be parsed later */
+
+ SkipTokComments(VLOG_DELIMITERS);
+ if (!strcmp(nexttok, "[")) {
+ /* Check for space between name and array identifier */
+ SkipTokComments(VLOG_PIN_NAME_DELIMITERS);
+ if (strcmp(nexttok, ")")) {
+ char *expnet;
+ expnet = (char *)malloc(strlen(new_port->net)
+ + strlen(nexttok) + 3);
+ sprintf(expnet, "%s [%s", new_port->net, nexttok);
+ free(new_port->net);
+ new_port->net = expnet;
+ }
+ SkipTokComments(VLOG_DELIMITERS);
+ }
+ if (strcmp(nexttok, ")")) {
+ fprintf(stdout, "Badly formed subcircuit pin line "
+ "at \"%s\" (line %d)\n", nexttok, vlinenum);
+ SkipNewLine(VLOG_DELIMITERS);
+ }
+ }
+
+ /* Register wire if it has not been already, and if it */
+ /* has been registered, check if this wire increases */
+ /* the net bounds. If the net name is an in-line */
+ /* vector, then process each component separately. */
+
+ ncomp = new_port->net;
+ while (isdigit(*ncomp)) ncomp++;
+ if (*ncomp == '{') ncomp++;
+ while (isspace(*ncomp)) ncomp++;
+ while (*ncomp != '\0') {
+ int is_esc = FALSE;
+ char saveptr;
+
+ /* NOTE: This follows same rules in strdtok() */
+ nptr = ncomp;
+ if (*nptr == '\\') is_esc = TRUE;
+ while (*nptr != ',' && *nptr != '}' && *nptr != '\0') {
+ if (*nptr == ' ') {
+ if (is_esc == TRUE)
+ is_esc = FALSE;
+ else
+ break;
+ }
+ nptr++;
+ }
+ saveptr = *nptr;
+ *nptr = '\0';
+
+ /* Parse ncomp as a net or bus */
+ if ((nb = BusHashLookup(ncomp, &top->nets)) == NULL)
+ nb = Net(top, ncomp);
+
+ GetBus(ncomp, &wb, &top->nets);
+ if (nb->start == -1) {
+ nb->start = wb.start;
+ nb->end = wb.end;
+ }
+ else {
+ if (nb->start < wb.start) nb->start = wb.start;
+ if (nb->end > wb.end) nb->end = wb.end;
+ }
+
+ *nptr = saveptr;
+ if (*new_port->net != '{')
+ break;
+
+ ncomp = nptr + 1;
+ /* Skip over any whitespace at the end of a name */
+ while ((*ncomp != '\0') && (*ncomp == ' '))
+ ncomp++;
+ while ((*ncomp != '\0') && (*nptr == ',' || *nptr == '}'))
+ ncomp++;
+ }
+ }
+ }
+ }
+ else {
+ fprintf(stdout, "Expected to find instance pin block but got "
+ "\"%s\" (line %d)\n", nexttok, vlinenum);
+ }
+ if (ignore == TRUE) continue; /* moving along. . . */
+
+ /* Verilog allows multiple instances of a single cell type to be */
+ /* chained together in a comma-separated list. */
+
+ SkipTokComments(VLOG_DELIMITERS);
+ if (!strcmp(nexttok, ",")) {
+ goto nextinst;
+ }
+
+ /* Otherwise, instance must end with a semicolon */
+
+ else if (strcmp(nexttok, ";")) {
+ fprintf(stdout, "Expected to find end of instance but got "
+ "\"%s\" (line %d)\n", nexttok, vlinenum);
+ }
+ }
+ continue;
+
+skip_endmodule:
+ /* There was an error, so skip to the end of the */
+ /* subcircuit definition */
+
+ while (1) {
+ SkipNewLine(VLOG_DELIMITERS);
+ SkipTokComments(VLOG_DELIMITERS);
+ if (EndParseFile()) break;
+ if (!strcmp(nexttok, "endmodule")) {
+ in_module = 0;
+ break;
+ }
+ }
+ continue;
+
+baddevice:
+ fprintf(stderr, "Badly formed line in input (line %d).\n", vlinenum);
+ }
+
+ /* Watch for bad ending syntax */
+
+ if (in_module == (char)1) {
+ fprintf(stderr, "Missing \"endmodule\" statement in module.\n");
+ InputParseError(stderr);
+ }
+
+ /* Make sure topmost cell is on stack before returning */
+ PushStack(top, CellStackPtr);
+
+ if (warnings)
+ fprintf(stderr, "File %s read with %d warning%s.\n", fname,
+ warnings, (warnings == 1) ? "" : "s");
+}
+
+/*----------------------------------------------*/
+/* Free memory associated with a cell */
+/*----------------------------------------------*/
+
+/*----------------------------------------------*/
+/* Free memory associated with property hash */
+/*----------------------------------------------*/
+
+int freeprop(struct hashlist *p)
+{
+ char *propval;
+
+ propval = (char *)(p->ptr);
+ free(propval);
+ return 1;
+}
+
+/*----------------------------------------------*/
+/* Top-level verilog module file read routine */
+/*----------------------------------------------*/
+
+struct cellrec *ReadVerilogTop(char *fname, int blackbox)
+{
+ struct cellstack *CellStackPtr = NULL;
+ struct cellrec *top;
+
+ if ((OpenParseFile(fname)) < 0) {
+ fprintf(stderr, "Error in Verilog file read: No file %s\n", fname);
+ return NULL;
+ }
+
+ if (dictinit == FALSE) {
+ /* verilogdefs may be pre-initialized by calling VerilogDefine() */
+ InitializeHashTable(&verilogdefs, TINYHASHSIZE);
+ dictinit = TRUE;
+ }
+ InitializeHashTable(&verilogparams, TINYHASHSIZE);
+ InitializeHashTable(&verilogvectors, TINYHASHSIZE);
+
+ ReadVerilogFile(fname, &CellStackPtr, blackbox);
+ CloseParseFile();
+
+ RecurseHashTable(&verilogparams, freeprop);
+ HashKill(&verilogparams);
+ RecurseHashTable(&verilogdefs, freeprop);
+ HashKill(&verilogdefs);
+
+ if (CellStackPtr == NULL) return NULL;
+
+ top = CellStackPtr->cell;
+ free(CellStackPtr);
+ return top;
+}
+
+/*--------------------------------------*/
+/* Wrappers for ReadVerilogTop() */
+/*--------------------------------------*/
+
+struct cellrec *ReadVerilog(char *fname)
+{
+ return ReadVerilogTop(fname, 0);
+}
+
+/*--------------------------------------*/
+/* Verilog file include routine */
+/*--------------------------------------*/
+
+void IncludeVerilog(char *fname, struct cellstack **CellStackPtr, int blackbox)
+{
+ int filenum = -1;
+ char name[256];
+
+ name[0] = '\0';
+
+ /* If fname does not begin with "/", then assume that it is */
+ /* in the same relative path as its parent. */
+
+ if (fname[0] != '/') {
+ char *ppath;
+ if (*CellStackPtr && ((*CellStackPtr)->cell != NULL)) {
+ strcpy(name, (*CellStackPtr)->cell->name);
+ ppath = strrchr(name, '/');
+ if (ppath != NULL)
+ strcpy(ppath + 1, fname);
+ else
+ strcpy(name, fname);
+ filenum = OpenParseFile(name);
+ }
+ }
+
+ /* If we failed the path relative to the parent, then try */
+ /* the filename alone (relative to the path where netgen */
+ /* was executed). */
+
+ if (filenum < 0) {
+ if ((filenum = OpenParseFile(fname)) < 0) {
+ if (filenum < 0) {
+ fprintf(stderr,"Error in Verilog file include: No file %s\n",
+ (*name == '\0') ? fname : name);
+ return;
+ }
+ }
+ }
+ ReadVerilogFile(fname, CellStackPtr, blackbox);
+ CloseParseFile();
+}
+
+/*------------------------------------------------------*/
+/* Free the cellrec structure created by ReadVerilog() */
+/*------------------------------------------------------*/
+
+void FreeVerilog(struct cellrec *topcell)
+{
+ struct portrec *port, *dport;
+ struct instance *inst, *dinst;
+
+ port = topcell->portlist;
+ while (port) {
+ if (port->name) free(port->name);
+ if (port->net) free(port->net);
+ dport = port->next;
+ free(port);
+ port = dport;
+ }
+ inst = topcell->instlist;
+ while (inst) {
+ if (inst->instname) free(inst->instname);
+ if (inst->cellname) free(inst->cellname);
+ port = inst->portlist;
+ while (port) {
+ if (port->name) free(port->name);
+ if (port->net) free(port->net);
+ dport = port->next;
+ free(port);
+ port = dport;
+ }
+ RecurseHashTable(&inst->propdict, freeprop);
+ HashKill(&inst->propdict);
+ dinst = inst->next;
+ free(inst);
+ inst = dinst;
+ }
+
+ /* Delete nets hashtable */
+ RecurseHashTable(&topcell->nets, freenet);
+ HashKill(&topcell->nets);
+
+ /* Delete properties hashtable. */
+ RecurseHashTable(&topcell->propdict, freeprop);
+ HashKill(&topcell->propdict);
+}
+
+
+// readverilog.c
diff --git a/src/readverilog.h b/src/readverilog.h
new file mode 100644
index 0000000..a50fde9
--- /dev/null
+++ b/src/readverilog.h
@@ -0,0 +1,118 @@
+/*----------------------------------------------------------------------*/
+/* readverilog.h -- Input for Verilog format (structural verilog only) */
+/*----------------------------------------------------------------------*/
+
+/*------------------------------------------------------*/
+/* Definitions and structures */
+/*------------------------------------------------------*/
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/* 'X' is used here to separate the single-character delimiters */
+/* from the two-character delimiters. */
+
+#define VLOG_DELIMITERS "///**/(**)#(X,;:(){}[]="
+#define VLOG_PIN_NAME_DELIMITERS "///**/(**)X()"
+#define VLOG_PIN_CHECK_DELIMITERS "///**/(**)X(),{}"
+
+#define VERILOG_EXTENSION ".v"
+
+/*------------------------------------------------------*/
+/* Ports and instances are hashed for quick lookup but */
+/* also placed in a linked list so that they can be */
+/* output in the same order as the original file. */
+/*------------------------------------------------------*/
+
+struct portrec {
+ char *name;
+ char *net; /* May be a {...} list */
+ int direction;
+ struct portrec *next;
+};
+
+struct instance { /* Hashed by instance name */
+ char *instname;
+ char *cellname;
+ int arraystart; /* -1 if not arrayed */
+ int arrayend; /* -1 if not arrayed */
+ struct portrec *portlist;
+ struct hashtable propdict; /* Instance properties */
+ struct instance *next;
+};
+
+/*------------------------------------------------------*/
+/* Basic cell definition (hashed by name) */
+/*------------------------------------------------------*/
+
+struct cellrec {
+ char *name; /* Cellname */
+
+ struct hashtable nets; /* Internal nets */
+ struct hashtable propdict; /* Properties */
+
+ struct portrec *portlist;
+ struct instance *instlist;
+ struct instance *lastinst; /* Track last item in list */
+};
+
+/*------------------------------------------------------*/
+
+#define PORT_NONE 0
+#define PORT_INPUT 1
+#define PORT_OUTPUT 2
+#define PORT_INOUT 3
+
+#define BUS_NONE -1
+
+/*------------------------------------------------------*/
+/* Net structure (hashed by root name, if a bus) */
+/*------------------------------------------------------*/
+
+struct netrec {
+ int start; /* start index, if a bus */
+ int end; /* end index, if a bus */
+};
+
+/*------------------------------------------------------*/
+/* Structure for stacking nested module definitions */
+/*------------------------------------------------------*/
+
+struct cellstack {
+ struct cellrec *cell;
+ struct cellstack *next;
+};
+
+/*------------------------------------------------------*/
+/* Structure for nested "include" files */
+/*------------------------------------------------------*/
+
+struct filestack {
+ FILE *file;
+ struct filestack *next;
+};
+
+/*------------------------------------------------------*/
+/* External variable declarations */
+/*------------------------------------------------------*/
+
+extern int vlinenum;
+
+/*------------------------------------------------------*/
+/* External function declarations */
+/*------------------------------------------------------*/
+
+extern void IncludeVerilog(char *, struct cellstack **, int);
+extern struct cellrec *ReadVerilog(char *);
+extern void FreeVerilog(struct cellrec *);
+extern void VerilogDefine(char *, char *);
+extern struct instance *AppendInstance(struct cellrec *cell, char *cellname);
+extern struct instance *PrependInstance(struct cellrec *cell, char *cellname);
+extern struct portrec *InstPort(struct instance *inst, char *portname, char *netname);
+extern void *BusHashLookup(char *s, struct hashtable *table);
+
+// readverilog.h
diff --git a/src/spice2delay.c b/src/spice2delay.c
index 26975bc..734ff07 100644
--- a/src/spice2delay.c
+++ b/src/spice2delay.c
@@ -169,7 +169,7 @@ void add_ritem (ritemptr *ritem_list_ptr, rptr r) {
}
}
-void process_subckt_inst(char **tokens, int num_toks, cell_io_ptr cell_io, struct hashlist **Nodehash, node_item_ptr **last_driver_ptr, int *numDrivers) {
+void process_subckt_inst(char **tokens, int num_toks, cell_io_ptr cell_io, struct hashtable *Nodehash, node_item_ptr **last_driver_ptr, int *numDrivers) {
nodeptr curr_node = NULL;
node_item_ptr next_src_item = NULL;
@@ -284,7 +284,7 @@ double spiceValtoD(char *string) {
return rtrnVal;
}
-void process_r(char **tokens, int num_toks, struct hashlist **Nodehash, ritemptr *fullrlist) {
+void process_r(char **tokens, int num_toks, struct hashtable *Nodehash, ritemptr *fullrlist) {
// create ritem which captures the resistor and the connection between two nodes
// for each node
// if node does not exist, create it
@@ -325,7 +325,7 @@ void process_r(char **tokens, int num_toks, struct hashlist **Nodehash, ritemptr
add_ritem(fullrlist, curr_r);
}
-void process_c(char **tokens, int num_toks, struct hashlist **Nodehash) {
+void process_c(char **tokens, int num_toks, struct hashtable *Nodehash) {
//
// change capacitance units to farads
//
@@ -491,14 +491,14 @@ int main (int argc, char* argv[]) {
// list of all Rs for debugging and to easily free them at end
ritemptr allrs = NULL;
- struct hashlist *Nodehash[OBJHASHSIZE];
+ struct hashtable Nodehash;
/* See hash.c for these routines and variables */
hashfunc = hash;
matchfunc = match;
/* Initialize net hash table */
- InitializeHashTable(Nodehash);
+ InitializeHashTable(&Nodehash, LARGEHASHSIZE);
// create first item in cell io list
cell_io_ptr cell_io_list = NULL;
@@ -582,19 +582,19 @@ int main (int argc, char* argv[]) {
if (!(strncmp(line, "R", 1))) {
printf("located resistor line\n");
- process_r(tokens, num_toks, Nodehash, &allrs);
+ process_r(tokens, num_toks, &Nodehash, &allrs);
} else if (!(strncmp(line, "C", 1))) {
printf("located capacitor line\n");
- process_c(tokens, num_toks, Nodehash);
+ process_c(tokens, num_toks, &Nodehash);
} else if (!(strncmp(line, "X", 1))) {
printf("located subckt instantiation line\n");
- printf("number of hash entries %d\n", RecurseHashTable(Nodehash, CountHashTableEntries));
- process_subckt_inst(tokens, num_toks, cell_io_list, Nodehash, &last_driver, &numDrivers);
- printf("number of hash entries %d\n", RecurseHashTable(Nodehash, CountHashTableEntries));
+ printf("number of hash entries %d\n", RecurseHashTable(&Nodehash, CountHashTableEntries));
+ process_subckt_inst(tokens, num_toks, cell_io_list, &Nodehash, &last_driver, &numDrivers);
+ printf("number of hash entries %d\n", RecurseHashTable(&Nodehash, CountHashTableEntries));
} else if (!(strncmp(line, ".subckt", 7))) {
printf("located subckt definition line\n");
@@ -652,11 +652,11 @@ int main (int argc, char* argv[]) {
curr_node_item = curr_node_item->next;
}
- currnode = HashFirst(Nodehash);
+ currnode = HashFirst(&Nodehash);
while (currnode != NULL) {
printf("%s\t\t%f\t%f\n", currnode->name, currnode->nodeCap, currnode->totCapDownstream);
- currnode = HashNext(Nodehash);
+ currnode = HashNext(&Nodehash);
}
@@ -722,7 +722,7 @@ int main (int argc, char* argv[]) {
}
printf("Number of Rs: %d\n", numRs);
- currnode = HashFirst(Nodehash);
+ currnode = HashFirst(&Nodehash);
int numNodes = 0;
while (currnode != NULL) {
@@ -739,7 +739,7 @@ int main (int argc, char* argv[]) {
printf("Node %s had %d Rs attached\n", currnode->name, numRs);
free(currnode->name);
free(currnode);
- currnode = HashNext(Nodehash);
+ currnode = HashNext(&Nodehash);
}
printf("Number of nodes: %d\n", numNodes);
diff --git a/src/vesta.c b/src/vesta.c
index 8dc3312..6baf447 100644
--- a/src/vesta.c
+++ b/src/vesta.c
@@ -47,7 +47,7 @@
/* 3: Report on verilog source file */
/* 4: Report on liberty file */
/*--------------------------------------------------------------*/
-/* (c) 2013-2018 Tim Edwards, Open Circuit Design */
+/* (c) 2013-2019 Tim Edwards, Open Circuit Design */
/* Released under GPL as part of the qflow package */
/*--------------------------------------------------------------*/
@@ -78,6 +78,7 @@
#include <sys/stat.h> // For mkdir()
#include <math.h> // Temporary, for fabs()
#include "hash.h" // For net hash table
+#include "readverilog.h"
#define LIB_LINE_MAX 65535
@@ -112,11 +113,12 @@ int fileCurrentLine;
// Sections of verilog file
#define MODULE 0
-#define IOLIST 1
-#define GATELIST 2
-#define INSTANCE 3
-#define INSTPIN 4
-#define PINCONN 5
+#define IOINLINE 1
+#define IOLIST 2
+#define GATELIST 3
+#define INSTANCE 4
+#define INSTPIN 5
+#define PINCONN 6
// Pin types (these are masks---e.g., a pin can be an INPUT and a CLOCK)
#define INPUT 0x01 // The default
@@ -381,7 +383,7 @@ unsigned char cleanup; /* Clean up net name syntax */
/*--------------------------------------------------------------*/
char *
-advancetoken(FILE *flib, char delimiter)
+advancetoken0(FILE *flib, char delimiter, char nocontline)
{
static char *token = NULL;
static char line[LIB_LINE_MAX];
@@ -432,7 +434,7 @@ advancetoken(FILE *flib, char delimiter)
/* Keep pulling stuff in if the line ends with a continuation character */
lptr = line;
while (*lptr != '\n' && *lptr != '\0') {
- if (*lptr == '\\') {
+ if ((*lptr == '\\') && (nocontline == 0)) {
// To be considered a line continuation marker, there must be
// only whitespace or newline between the backslash and the
// end of the string.
@@ -531,7 +533,7 @@ advancetoken(FILE *flib, char delimiter)
// Final: Remove trailing whitespace
tptr = token + strlen(token) - 1;
- while (isblank(*tptr)) {
+ while ((tptr > token) && isblank(*tptr)) {
*tptr = '\0';
tptr--;
}
@@ -539,6 +541,26 @@ advancetoken(FILE *flib, char delimiter)
}
/*--------------------------------------------------------------*/
+/* Wrapper for advancetoken0(): Default nocontline = 0 */
+/*--------------------------------------------------------------*/
+
+char *
+advancetoken(FILE *flib, char delimiter)
+{
+ return advancetoken0(flib, delimiter, 0);
+}
+
+/*--------------------------------------------------------------*/
+/* Wrapper for advancetoken0(): nocontline = 1 for delay file */
+/*--------------------------------------------------------------*/
+
+char *
+advancetokennocont(FILE *flib, char delimiter)
+{
+ return advancetoken0(flib, delimiter, 1);
+}
+
+/*--------------------------------------------------------------*/
/* Parse a pin name. Check if the cell has a pin of that name, */
/* and if not, add the pin to the cell, giving it default */
/* values. The pin name may contain quotes, parentheses, or */
@@ -665,8 +687,8 @@ double *table_collapse(lutableptr tableptr, double load)
// Interpolate value at cap load for each transition value
- vlow = *(tableptr->values + i * tableptr->size1 + (j - 1));
- vhigh = *(tableptr->values + i * tableptr->size1 + j);
+ vlow = *(tableptr->values + i * tableptr->size2 + (j - 1));
+ vhigh = *(tableptr->values + i * tableptr->size2 + j);
*(vector + i) = vlow + (vhigh - vlow) * cfrac;
}
return vector;
@@ -855,12 +877,14 @@ double calc_prop_delay(double trans, connptr testconn, short sense, char minmax)
if (sense != SENSE_NEGATIVE) {
if (testconn->prvector)
propdelayr = vector_get_value(testpin->propdelr, testconn->prvector, trans);
+ if (propdelayr < 0.0) propdelayr = 0.0;
if (sense == SENSE_POSITIVE) return propdelayr;
}
if (sense != SENSE_POSITIVE) {
if (testconn->pfvector)
propdelayf = vector_get_value(testpin->propdelf, testconn->pfvector, trans);
+ if (propdelayf < 0.0) propdelayf = 0.0;
if (sense == SENSE_NEGATIVE) return propdelayf;
}
@@ -891,12 +915,14 @@ double calc_transition(double trans, connptr testconn, short sense, char minmax)
if (sense != SENSE_NEGATIVE) {
if (testconn->trvector)
transr = vector_get_value(testpin->transr, testconn->trvector, trans);
+ if (transr < 0.0) transr = 0.0;
if (sense == SENSE_POSITIVE) return transr;
}
if (sense != SENSE_POSITIVE) {
if (testconn->tfvector)
transf = vector_get_value(testpin->transf, testconn->tfvector, trans);
+ if (transf < 0.0) transf = 0.0;
if (sense == SENSE_NEGATIVE) return transf;
}
@@ -992,6 +1018,11 @@ double calc_setup_time(double trans, pinptr testpin, double clktrans, short sens
/* clock is found, then return NULL---these endpoints are */
/* asynchronously clocked. */
/* */
+/* Modified 5/23/2019: Cascaded clocks should be traced */
+/* through a flop. i.e., if a clock is found to come from a */
+/* DFF output, then add the clock-to-Q delay and keep tracing */
+/* from the DFF clock. */
+/* */
/* If mode == 2, return TRUE if the search ended on a */
/* connection found in a mode 1 search. Otherwise, return */
/* FALSE. */
@@ -1035,13 +1066,34 @@ find_clock_source(connptr testlink, ddataptr *clocklist, btptr btrace, short dir
iupstream = driver->refinst;
if (iupstream == NULL) goto makehead; /* Not supposed to happen? */
- if (driver->refpin->type & DFFOUT) goto makehead; /* Reached a flop output */
- if (driver->refpin->type & LATCHOUT) goto makehead; /* Reached a latch output */
- for (iinput = iupstream->in_connects; iinput; iinput = iinput->next) {
- newdir = calc_dir(iinput->refpin, dir);
- result = find_clock_source(iinput, clocklist, newclock, newdir, mode);
- if (result == (unsigned char)1) return result;
+ /* If a flop or latch output is reached, add the clock-to-output delay */
+ /* and continue tracing from the DFF clock or LATCH input. */
+
+ if (driver->refpin->type & DFFOUT) {
+ for (iinput = driver->refinst->in_connects; iinput; iinput = iinput->next) {
+ if (iinput->refpin->type & DFFCLK) {
+ newdir = calc_dir(iinput->refpin, dir);
+ result = find_clock_source(iinput, clocklist, newclock, newdir, mode);
+ if (result == (unsigned char)1) return result;
+ }
+ }
+ }
+ else if (driver->refpin->type & LATCHOUT) {
+ for (iinput = driver->refinst->in_connects; iinput; iinput = iinput->next) {
+ if (iinput->refpin->type & LATCHIN) {
+ newdir = calc_dir(iinput->refpin, dir);
+ result = find_clock_source(iinput, clocklist, newclock, newdir, mode);
+ if (result == (unsigned char)1) return result;
+ }
+ }
+ }
+ else {
+ for (iinput = iupstream->in_connects; iinput; iinput = iinput->next) {
+ newdir = calc_dir(iinput->refpin, dir);
+ result = find_clock_source(iinput, clocklist, newclock, newdir, mode);
+ if (result == (unsigned char)1) return result;
+ }
}
return (unsigned char)0;
@@ -1787,16 +1839,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
newtable->invert = 1;
}
else if (!strcasecmp(token, "index_1")) {
+ char dnum = ',';
+
token = advancetoken(flib, 0); // Open parens
token = advancetoken(flib, 0); // Quote
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ iptr = token;
+
+ // Check if table is space or comma separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (newtable->invert == 1) {
// Count entries
- iptr = token;
newtable->size2 = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size2++;
}
@@ -1810,7 +1870,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
else
newtable->idx2.caps[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size2++;
sscanf(iptr, "%lg",
@@ -1824,9 +1884,8 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
}
else { // newtable->invert = 0
// Count entries
- iptr = token;
newtable->size1 = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size1++;
}
@@ -1836,7 +1895,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
iptr = token;
sscanf(iptr, "%lg", &newtable->idx1.times[0]);
newtable->idx1.times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size1++;
sscanf(iptr, "%lg",
@@ -1849,16 +1908,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
token = advancetoken(flib, ';'); // EOL semicolon
}
else if (!strcasecmp(token, "index_2")) {
+ char dnum = ',';
+
token = advancetoken(flib, 0); // Open parens
token = advancetoken(flib, 0); // Quote
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ iptr = token;
+
+ // Check if table is space or comma separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
if (newtable->invert == 0) {
// Count entries
- iptr = token;
newtable->size2 = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size2++;
}
@@ -1871,7 +1938,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
newtable->idx2.caps[0] *= cap_unit;
else
newtable->idx2.cons[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size2++;
sscanf(iptr, "%lg",
@@ -1885,9 +1952,8 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
}
else { // newtable->invert == 1
// Count entries
- iptr = token;
newtable->size1 = 1;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size1++;
}
@@ -1897,7 +1963,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
iptr = token;
sscanf(iptr, "%lg", &newtable->idx1.times[0]);
newtable->idx1.times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
newtable->size1++;
sscanf(iptr, "%lg",
@@ -2487,6 +2553,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
while (*token != '}') {
token = advancetoken(flib, 0);
if (!strcasecmp(token, "index_1")) {
+ char dnum = ',';
// Local index values override those in the template
@@ -2495,18 +2562,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ iptr = token;
+
+ // Check if table is space or comma separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
//-------------------------
if (reftable && (reftable->invert == 1)) {
// Entries had better match the ref table
- iptr = token;
i = 0;
sscanf(iptr, "%lg", &tableptr->idx2.caps[0]);
if (tableptr->var2 == OUTPUT_CAP)
tableptr->idx2.caps[0] *= cap_unit;
else
tableptr->idx2.cons[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &tableptr->idx2.caps[i]);
@@ -2521,7 +2594,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
i = 0;
sscanf(iptr, "%lg", &tableptr->idx1.times[0]);
tableptr->idx1.times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &tableptr->idx1.times[i]);
@@ -2533,6 +2606,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
token = advancetoken(flib, ';'); // EOL semicolon
}
else if (!strcasecmp(token, "index_2")) {
+ char dnum = ',';
// Local index values override those in the template
@@ -2541,15 +2615,21 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
if (!strcmp(token, "\""))
token = advancetoken(flib, '\"');
+ iptr = token;
+
+ // Check if table is space or comma separated
+ if (strchr(iptr, dnum) == NULL)
+ if (strchr(iptr, ' ') != NULL)
+ dnum = ' ';
+
//-------------------------
if (reftable && (reftable->invert == 1)) {
// Entries had better match the ref table
- iptr = token;
i = 0;
sscanf(iptr, "%lg", &tableptr->idx1.times[0]);
tableptr->idx1.times[0] *= time_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &tableptr->idx1.times[i]);
@@ -2561,7 +2641,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
i = 0;
sscanf(iptr, "%lg", &tableptr->idx2.caps[0]);
tableptr->idx2.caps[0] *= cap_unit;
- while ((iptr = strchr(iptr, ',')) != NULL) {
+ while ((iptr = strchr(iptr, dnum)) != NULL) {
iptr++;
i++;
sscanf(iptr, "%lg", &tableptr->idx2.caps[i]);
@@ -2686,244 +2766,166 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist)
/*--------------------------------------------------------------*/
void
-verilogRead(FILE *fsrc, cell *cells, net **netlist, instance **instlist,
- connect **inputlist, connect **outputlist, struct hashlist **Nethash)
+verilogRead(char *filename, cell *cells, net **netlist, instance **instlist,
+ connect **inputlist, connect **outputlist, struct hashtable *Nethash)
{
- char *token;
- char *modname = NULL;
- int section = MODULE;
+ struct portrec *port;
+ struct instance *inst;
+ struct cellrec *topcell;
+ struct netrec *net;
+ connptr newconn, testconn;
instptr newinst;
+ pinptr testpin;
netptr newnet, testnet;
cellptr testcell;
- connptr newconn, testconn;
- pinptr testpin;
- int vstart, vend, vtarget, isinput;
-
- /* Read tokens off of the line */
- token = advancetoken(fsrc, 0);
-
- while (token != NULL) {
-
- switch (section) {
- case MODULE:
- if (!strcasecmp(token, "module")) {
- token = advancetoken(fsrc, 0);
- fprintf(stderr, "Parsing module \"%s\"\n", token);
+ int vstart, vtarget;
- token = advancetoken(fsrc, 0);
- if (strcmp(token, "("))
- fprintf(stderr, "Module not followed by pin list\n");
- else
- token = advancetoken(fsrc, ')');
- token = advancetoken(fsrc, ';'); // Get end-of-line
+ /* Get verilog netlist structure using routines in readverilog.c */
+ topcell = ReadVerilog(filename);
+ if (topcell && topcell->name) {
+ fprintf(stdout, "Parsing module \"%s\"\n", topcell->name);
+ }
- // Ignore the pin list, go straight to the input/output declarations
- section = IOLIST;
- }
- break;
+ /* Build local connection lists from returned netlist structure */
- case IOLIST:
- if (!strcasecmp(token, "input") || !strcasecmp(token, "output")) {
+ for (port = topcell->portlist; port; port = port->next) {
+ testconn = NULL;
- testconn = NULL;
- vstart = vend = 0;
+ // Create a net entry for the input or output, add to the list of nets
- if (!strcasecmp(token, "input")) {
- isinput = 1;
- }
- else { // output
- isinput = 0;
- }
+ net = HashLookup(port->name, &topcell->nets);
+ if (net->start == 0 && net->end == 0) {
+ newnet = create_net(netlist);
+ newnet->name = strdup(port->name);
+ HashPtrInstall(newnet->name, newnet, Nethash);
- token = advancetoken(fsrc, 0);
- if (*token == '[') {
- sscanf(token + 1, "%d", &vstart);
- token = advancetoken(fsrc, ':');
- token = advancetoken(fsrc, ']'); // Read to end of vector
- sscanf(token, "%d", &vend);
- token = advancetoken(fsrc, 0); // Read signal name
- }
+ testconn = (connptr)malloc(sizeof(connect));
+ testconn->refnet = newnet;
+ testconn->refpin = NULL; // No associated pin
+ testconn->refinst = NULL; // No associated instance
+ testconn->tag = NULL;
+ testconn->metric = -1.0;
+ testconn->visited = (unsigned char)0;
+ testconn->prvector = NULL;
+ testconn->pfvector = NULL;
+ testconn->trvector = NULL;
+ testconn->tfvector = NULL;
+
+ if (port->direction == PORT_INPUT) { // driver (input)
+ testconn->next = *inputlist;
+ *inputlist = testconn;
+ }
+ else { // receiver (output)
+ testconn->next = *outputlist;
+ *outputlist = testconn;
+ }
+ }
+ else {
+ vtarget = net->end + ((net->start < net->end) ? 1 : -1);
+ vstart = net->start;
+ while (vstart != vtarget) {
+ newnet = create_net(netlist);
+ newnet->name = (char *)malloc(strlen(port->name) + 6);
+ sprintf(newnet->name, "%s[%d]", port->name, vstart);
+ HashPtrInstall(newnet->name, newnet, Nethash);
+
+ vstart += (vtarget > net->end) ? 1 : -1;
+
+ testconn = (connptr)malloc(sizeof(connect));
+ testconn->refnet = newnet;
+ testconn->refpin = NULL; // No associated pin
+ testconn->refinst = NULL; // No associated instance
+ testconn->tag = NULL;
+ testconn->metric = -1.0;
+ testconn->visited = (unsigned char)0;
+ testconn->prvector = NULL;
+ testconn->pfvector = NULL;
+ testconn->trvector = NULL;
+ testconn->tfvector = NULL;
+
+ if (port->direction == PORT_INPUT) { // driver (input)
+ testconn->next = *inputlist;
+ *inputlist = testconn;
+ }
+ else { // receiver (output)
+ testconn->next = *outputlist;
+ *outputlist = testconn;
+ }
+ }
+ }
+ }
- // Create a net entry for the input or output, add to the list of nets
-
- if (vstart == 0 && vend == 0) {
- newnet = create_net(netlist);
- newnet->name = strdup(token);
- HashPtrInstall(newnet->name, newnet, Nethash);
-
- testconn = (connptr)malloc(sizeof(connect));
- testconn->refnet = newnet;
- testconn->refpin = NULL; // No associated pin
- testconn->refinst = NULL; // No associated instance
- testconn->tag = NULL;
- testconn->metric = -1.0;
- testconn->visited = (unsigned char)0;
- testconn->prvector = NULL;
- testconn->pfvector = NULL;
- testconn->trvector = NULL;
- testconn->tfvector = NULL;
-
- if (isinput) { // driver (input)
- testconn->next = *inputlist;
- *inputlist = testconn;
- }
- else { // receiver (output)
- testconn->next = *outputlist;
- *outputlist = testconn;
- }
- }
- else {
- vtarget = vend + ((vstart < vend) ? 1 : -1);
- while (vstart != vtarget) {
- newnet = create_net(netlist);
- newnet->name = (char *)malloc(strlen(token) + 6);
- sprintf(newnet->name, "%s[%d]", token, vstart);
- HashPtrInstall(newnet->name, newnet, Nethash);
-
- vstart += (vtarget > vend) ? 1 : -1;
-
- testconn = (connptr)malloc(sizeof(connect));
- testconn->refnet = newnet;
- testconn->refpin = NULL; // No associated pin
- testconn->refinst = NULL; // No associated instance
- testconn->tag = NULL;
- testconn->metric = -1.0;
- testconn->visited = (unsigned char)0;
- testconn->prvector = NULL;
- testconn->pfvector = NULL;
- testconn->trvector = NULL;
- testconn->tfvector = NULL;
-
- if (isinput) { // driver (input)
- testconn->next = *inputlist;
- *inputlist = testconn;
- }
- else { // receiver (output)
- testconn->next = *outputlist;
- *outputlist = testconn;
- }
- }
- }
- token = advancetoken(fsrc, ';'); // Get rest of input/output entry
- break;
- }
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ for (testcell = cells; testcell; testcell = testcell->next)
+ if (!strcasecmp(testcell->name, inst->cellname))
+ break;
- /* Drop through on anything that isn't an input, output, or blank line */
+ // NOTE: testcell may be NULL for non-functional cells like
+ // filler cells which have no I/O and so have no timing. Only
+ // report cells that are relevant to timing and do not show up
+ // in the liberty database (portlist is non-NULL).
- case GATELIST:
+ if ((testcell == NULL) && (inst->portlist != NULL)) {
+ fprintf(stderr, "Cell \"%s\" was not in the liberty database!\n",
+ inst->cellname);
+ continue;
+ }
- if (!strcasecmp(token, "endmodule")) {
- section = MODULE;
+ newinst = (instptr)malloc(sizeof(instance));
+ newinst->next = *instlist;
+ *instlist = newinst;
+ newinst->refcell = testcell;
+ newinst->in_connects = NULL;
+ newinst->out_connects = NULL;
+ newinst->name = strdup(inst->instname);
+
+ for (port = inst->portlist; port; port = port->next) {
+ newconn = (connptr)malloc(sizeof(connect));
+ for (testpin = testcell->pins; testpin; testpin = testpin->next) {
+ if (!strcmp(testpin->name, port->name))
break;
- }
-
- /* Confirm that the token is a known cell, and continue parsing line if so */
- /* Otherwise, parse to semicolon line end and continue */
-
- for (testcell = cells; testcell; testcell = testcell->next)
- if (!strcasecmp(testcell->name, token))
- break;
-
- if (testcell != NULL) {
- section = INSTANCE;
- newinst = (instptr)malloc(sizeof(instance));
- newinst->next = *instlist;
- *instlist = newinst;
- newinst->refcell = testcell;
- newinst->in_connects = NULL;
- newinst->out_connects = NULL;
+ }
+ // Sanity check
+ if (testpin == NULL) {
+ fprintf(stderr, "No such pin \"%s\" in cell \"%s\"!\n",
+ port->name, testcell->name);
+ }
+ else {
+ if (testpin->type & OUTPUT) {
+ newconn->next = newinst->out_connects;
+ newinst->out_connects = newconn;
}
else {
- /* Ignore all wire and assign statements */
- /* Qflow does not generate these, but other */
- /* synthesis tools may. */
-
- if (!strcasecmp(token, "assign") && (verbose > 1)) {
- fprintf(stdout, "Wire assignments are not handled!\n");
- }
- else if (strcasecmp(token, "wire") && (verbose > 1)) {
- fprintf(stdout, "Unknown cell \"%s\" instanced.\n",
- token);
- }
- token = advancetoken(fsrc, ';'); // Get rest of entry, and ignore
+ newconn->next = newinst->in_connects;
+ newinst->in_connects = newconn;
}
- break;
-
- case INSTANCE:
- newinst->name = strdup(token);
- token = advancetoken(fsrc, '('); // Find beginning of pin list
- section = INSTPIN;
- break;
-
- case INSTPIN:
- if (*token == '.') {
- newconn = (connptr)malloc(sizeof(connect));
- // Pin name is in (token + 1)
- for (testpin = testcell->pins; testpin; testpin = testpin->next) {
- if (!strcmp(testpin->name, token + 1))
- break;
- }
- // Sanity check
- if (testpin == NULL) {
- fprintf(stderr, "No such pin \"%s\" in cell \"%s\"!\n",
- token + 1, testcell->name);
- }
- else {
- if (testpin->type & OUTPUT) {
- newconn->next = newinst->out_connects;
- newinst->out_connects = newconn;
- }
- else {
- newconn->next = newinst->in_connects;
- newinst->in_connects = newconn;
- }
- }
- newconn->refinst = newinst;
- newconn->refpin = testpin;
- newconn->refnet = NULL;
- newconn->tag = NULL;
- newconn->metric = -1.0;
- newconn->visited = (unsigned char)0;
- newconn->prvector = NULL;
- newconn->pfvector = NULL;
- newconn->trvector = NULL;
- newconn->tfvector = NULL;
- token = advancetoken(fsrc, '('); // Read to beginning of pin name
- section = PINCONN;
- }
- else if (*token == ';') {
- // End of instance record
- section = GATELIST;
- }
- else if (*token != ',' && *token != ')') {
- fprintf(stderr, "Unexpected entry in instance pin connection list!\n");
- token = advancetoken(fsrc, ';'); // Read to end-of-line
- section = GATELIST;
- }
- break;
-
- case PINCONN:
- // Token is net name
- testnet = (netptr)HashLookup(token, Nethash);
- if (testnet == NULL) {
- // This is a new net, and we need to record it
- newnet = create_net(netlist);
- newnet->name = strdup(token);
- HashPtrInstall(newnet->name, newnet, Nethash);
- newconn->refnet = newnet;
- }
- else
- newconn->refnet = testnet;
- section = INSTPIN;
- break;
- }
- if (section == PINCONN)
- token = advancetoken(fsrc, ')'); // Name token parsing
- else
- token = advancetoken(fsrc, 0);
+ }
+ newconn->refinst = newinst;
+ newconn->refpin = testpin;
+ newconn->refnet = NULL;
+ newconn->tag = NULL;
+ newconn->metric = -1.0;
+ newconn->visited = (unsigned char)0;
+ newconn->prvector = NULL;
+ newconn->pfvector = NULL;
+ newconn->trvector = NULL;
+ newconn->tfvector = NULL;
+
+ testnet = (netptr)HashLookup(port->net, Nethash);
+ if (testnet == NULL) {
+ // This is a new net, and we need to record it
+ newnet = create_net(netlist);
+ newnet->name = strdup(port->net);
+ HashPtrInstall(newnet->name, newnet, Nethash);
+ newconn->refnet = newnet;
+ }
+ else
+ newconn->refnet = testnet;
+ }
}
+ FreeVerilog(topcell); // All structures transferred
}
/*--------------------------------------------------------------*/
@@ -3012,6 +3014,8 @@ int assign_net_types(netptr netlist, connlistptr *clockedlist)
numterms = 0;
for (testnet = netlist; testnet; testnet = testnet->next) {
+ /* Nets with no fanout are by definition module outputs */
+ if (testnet->fanout == 0) testnet->type |= OUTTERM;
for (i = 0; i < testnet->fanout; i++) {
testrcvr = testnet->receivers[i];
testpin = testrcvr->refpin;
@@ -3139,17 +3143,18 @@ compdelay(ddataptr *a, ddataptr *b)
}
void
-delayRead(FILE *fdly, struct hashlist **Nethash)
+delayRead(FILE *fdly, struct hashtable *Nethash)
{
char c[128];
char d[128];
char *token;
+ char *result;
+ char *tokencopy = NULL;
netptr newnet, testnet;
connptr testconn;
pinptr testpin;
int i;
- char *result;
int numRxers;
if (debug == 1)
@@ -3159,7 +3164,7 @@ delayRead(FILE *fdly, struct hashlist **Nethash)
/* be a standard delimiter, breaking up certain yosys-generated */
/* net names. */
- token = advancetoken(fdly, '\n');
+ token = advancetokennocont(fdly, '\n');
result = token;
while (token != NULL) {
@@ -3175,28 +3180,54 @@ delayRead(FILE *fdly, struct hashlist **Nethash)
if ((testnet == NULL) && cleanup) {
char *mchr, *dchr;
- if ((mchr = strrchr(token, '<')) != NULL) {
- if ((dchr = strrchr(token, '>')) != NULL) {
- if (mchr < dchr) {
- *mchr = '[';
- *dchr = ']';
+ /* Handle the insane backslash-escape names in verilog.
+ * To make these compatible with SPICE, qflow opts to
+ * replace the ending space character with another
+ * backslash. The 2nd backslash has to be replaced by
+ * the original space character to match the original
+ * verilog net name.
+ */
+
+ if (*token == '\\') {
+ if ((mchr = strchr(token + 1, '\\')) != NULL) {
+ dchr = strchr(token + 1, ' ');
+ if ((dchr == NULL) || (dchr > mchr)) *mchr = ' ';
+ }
+ }
+ testnet = (netptr)HashLookup(token, Nethash);
+
+ /* Other, legacy stuff. */
+ if (testnet == NULL) {
+ tokencopy = strdup(token);
+ if ((mchr = strrchr(tokencopy, '<')) != NULL) {
+ if ((dchr = strrchr(tokencopy, '>')) != NULL) {
+ if (mchr < dchr) {
+ *mchr = '[';
+ *dchr = ']';
+ }
}
}
+ testnet = (netptr)HashLookup(tokencopy, Nethash);
}
- for (mchr = token; *mchr != '\0'; mchr++) {
- if ((*mchr == ':') || (*mchr == '.') || (*mchr == '$')
+
+ if (testnet == NULL) {
+ for (mchr = tokencopy; *mchr != '\0'; mchr++) {
+ if ((*mchr == ':') || (*mchr == '.') || (*mchr == '$')
|| (*mchr == '<') || (*mchr == '>'))
- *mchr = '_';
+ *mchr = '_';
+ }
+ testnet = (netptr)HashLookup(tokencopy, Nethash);
}
- testnet = (netptr)HashLookup(token, Nethash);
+
if (testnet == NULL) {
- for (mchr = token; *mchr != '\0'; mchr++) {
+ for (mchr = tokencopy; *mchr != '\0'; mchr++) {
if ((*mchr == '[') || (*mchr == ']'))
*mchr = '_';
}
+ testnet = (netptr)HashLookup(tokencopy, Nethash);
}
- testnet = (netptr)HashLookup(token, Nethash);
}
+ if (tokencopy != NULL) free(tokencopy);
if (testnet == NULL) {
fprintf(stderr, "ERROR: Net %s not found in hash table\n", token);
@@ -3270,18 +3301,20 @@ delayRead(FILE *fdly, struct hashlist **Nethash)
if (result == NULL) break;
numRxers += 1;
}
- if (result == NULL) break;
+ if (result == NULL) break;
if (numRxers != testnet->fanout) {
- fprintf(stderr, "ERROR: Net %s only had %d receivers in delay file, "
+ if (numRxers != 1 || testnet->fanout > 0 || testnet->type != OUTTERM)
+ fprintf(stderr, "ERROR: Net %s had %d receiver%s in delay file, "
" but expected a fanout of %d\n", testnet->name,
- numRxers, testnet->fanout);
+ numRxers, (numRxers == 1) ? "" : "s",
+ testnet->fanout);
}
- token = advancetoken(fdly, '\n');
+ token = advancetokennocont(fdly, '\n');
}
if (result == NULL) {
- fprintf(stderr, "ERROR: Unexpected end-of-file while reading delay file.\n");
+ fprintf(stderr, "ERROR: Unexpected end-of-file while reading delay file.\n");
}
}
@@ -3702,7 +3735,7 @@ main(int objc, char *argv[])
double slack;
// Net name hash table
- struct hashlist *Nethash[OBJHASHSIZE];
+ struct hashtable Nethash;
verbose = 0;
exhaustive = 0;
@@ -3803,6 +3836,7 @@ main(int objc, char *argv[])
fprintf(stderr, "Cannot open %s for reading\n", argv[firstarg]);
exit (1);
}
+ fclose(fsrc);
/*------------------------------------------------------------------*/
/* Generate one table template for the "scalar" case */
@@ -3885,11 +3919,12 @@ main(int objc, char *argv[])
matchfunc = match;
/* Initialize net hash table */
- InitializeHashTable(Nethash);
+ InitializeHashTable(&Nethash, LARGEHASHSIZE);
fileCurrentLine = 0;
- verilogRead(fsrc, cells, &netlist, &instlist, &inputlist, &outputlist, Nethash);
+ verilogRead(argv[firstarg], cells, &netlist, &instlist, &inputlist, &outputlist,
+ &Nethash);
if (delayfile != NULL) {
fdly = fopen(delayfile, "r");
@@ -3903,8 +3938,7 @@ main(int objc, char *argv[])
fdly = NULL;
fflush(stdout);
- fprintf(stdout, "Verilog netlist read: Processed %d lines.\n", fileCurrentLine);
- if (fsrc != NULL) fclose(fsrc);
+ fprintf(stdout, "Verilog netlist read: Processed %d lines.\n", vlinenum);
/*--------------------------------------------------*/
/* Debug: Print summary of verilog source */
@@ -3963,12 +3997,12 @@ main(int objc, char *argv[])
/*--------------------------------------------------*/
if (fdly != NULL) {
- delayRead(fdly, Nethash);
+ delayRead(fdly, &Nethash);
fclose(fdly);
}
/* Hash table no longer needed */
- HashKill(Nethash);
+ HashKill(&Nethash);
computeLoads(netlist, instlist, outLoad);
@@ -4101,7 +4135,7 @@ main(int objc, char *argv[])
if (fsum) fprintf(fsum, "Design meets timing requirements.\n");
}
}
- else if (orderedpaths[0] != NULL) {
+ else if ((numpaths > 0) && (orderedpaths[0] != NULL)) {
fprintf(stdout, "Computed maximum clock frequency (zero margin) = %g MHz\n",
(1.0E6 / orderedpaths[0]->delay));
if (fsum) fprintf(fsum, "Computed maximum clock frequency "
@@ -4211,6 +4245,7 @@ main(int objc, char *argv[])
if (longFormat) print_path(testddata->backtrace, stdout);
if (fsum) print_path(testddata->backtrace, fsum);
+ /* Print skew and hold unless destination is a pin */
if (testddata->backtrace->receiver->refinst != NULL) {
if (longFormat) {
fprintf(stdout, " clock skew at destination = %g\n", testddata->skew);
diff --git a/src/vlog2Cel.c b/src/vlog2Cel.c
new file mode 100644
index 0000000..3829b88
--- /dev/null
+++ b/src/vlog2Cel.c
@@ -0,0 +1,571 @@
+//----------------------------------------------------------------
+// vlog2Cel
+//----------------------------------------------------------------
+// Generate a .cel file for the GrayWolf placement tool from a
+// verilog source, plus LEF files for the technology, standard
+// cells, and (if present) hard macros.
+//
+// Revision 0, 2018-12-1: First release by R. Timothy Edwards.
+//
+// This program is written in ISO C99.
+//----------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* for getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readverilog.h"
+#include "readlef.h"
+
+int write_output(struct cellrec *, int, char *);
+void helpmessage(FILE *outf);
+
+char *VddNet = NULL;
+char *GndNet = NULL;
+
+struct hashtable LEFhash;
+
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, units, result;
+
+ char *outfile = NULL;
+ char *vlogname = NULL;
+ struct cellrec *topcell;
+
+ VddNet = strdup("VDD");
+ GndNet = strdup("VSS");
+
+ InitializeHashTable(&LEFhash, SMALLHASHSIZE);
+ units = 100; /* Default value is centimicrons */
+
+ while ((i = getopt(argc, argv, "hHu:l:o:")) != EOF) {
+ switch (i) {
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ return 0;
+ case 'u':
+ if (sscanf(optarg, "%d", &units) != 1) {
+ fprintf(stderr, "Cannot read integer units from \"%s\"\n", optarg);
+ }
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'o':
+ outfile = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr, "Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ vlogname = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename as input\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ /* If any LEF files were read, hash the GateInfo list */
+ if (GateInfo != NULL) {
+ GATE gate;
+ for (gate = GateInfo; gate; gate = gate->next) {
+ HashPtrInstall(gate->gatename, gate, &LEFhash);
+ }
+ }
+
+ topcell = ReadVerilog(vlogname);
+ result = write_output(topcell, units, outfile);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* write_output: Generate the .cel file output. */
+/* */
+/* ARGS: */
+/* RETURNS: 0 on success, 1 on error */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int write_output(struct cellrec *topcell, int units, char *outfile)
+{
+ FILE *outfptr = stdout;
+ int result = 0;
+
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+
+ GATE gateginfo;
+
+ int i, j, layers, feedx, cellidx, kidx;
+ int llx, lly, cllx, clly, curx, cury;
+ int urx, ury, width, height, px, py;
+ int *pitchx, *pitchy;
+ int lvert = -1;
+ int arrayidx;
+ char *netsptr;
+
+ if (outfile != NULL) {
+ outfptr = fopen(outfile, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Failed to open file %s for output\n",
+ outfile);
+ return 1;
+ }
+ }
+
+ /* Count route layers (just need a maximum for memory allocation) */
+ layers = LefGetMaxRouteLayer();
+
+ pitchx = (int *)calloc((layers + 1), sizeof(int));
+ pitchy = (int *)calloc((layers + 1), sizeof(int));
+
+ /* Pull pitch information from LEF database */
+
+ for (i = 0; i <= layers; i++) {
+ pitchx[i] = (int)(LefGetRoutePitchX(i) * (double)units + 0.5);
+ pitchy[i] = (int)(LefGetRoutePitchY(i) * (double)units + 0.5);
+ }
+
+ /* Find first vertical route that is not route 0 */
+ for (i = 1; i <= layers; i++) {
+ if (LefGetRouteOrientation(i) == 0) {
+ lvert = i;
+ break;
+ }
+ }
+
+ /* If X pitch on layer lvert is zero, then infinite loops happen */
+ if (lvert < 0) {
+ fprintf(stderr, "Error: Failed to get layer information; cannot continue.\n");
+ return 1;
+ }
+ else if (pitchx[lvert] <= 0) {
+ fprintf(stderr, "Error: Bad value %d for X pitch on vertical route"
+ " layer %d; cannot continue.\n",
+ pitchx[lvert], lvert);
+ return 1;
+ }
+
+ /* Write instances in the order of the input file */
+
+ cellidx = 1;
+ kidx = 1;
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ if (inst->cellname)
+ gateginfo = HashLookup(inst->cellname, &LEFhash);
+ else
+ gateginfo = NULL;
+
+ if (gateginfo == NULL) {
+ fprintf(stderr, "Error: Cell \"%s\" of instance \"%s\" not found"
+ " in LEF databases!\n", inst->cellname, inst->instname);
+ result = 1; // Set error result but continue output.
+ continue;
+ }
+
+ width = (int)(gateginfo->width * (double)units + 0.5);
+ height = (int)(gateginfo->height * (double)units + 0.5);
+
+ cllx = -(width >> 1);
+ clly = -(height >> 1);
+ curx = width + cllx;
+ cury = height + clly;
+
+ arrayidx = inst->arraystart;
+ while (1) {
+
+ if (arrayidx != -1)
+ fprintf(outfptr, "cell %d %s:%s[%d]\n", cellidx, inst->cellname,
+ inst->instname, arrayidx);
+ else
+ fprintf(outfptr, "cell %d %s:%s\n", cellidx, inst->cellname,
+ inst->instname);
+ fprintf(outfptr, "left %d right %d bottom %d top %d\n",
+ cllx, curx, clly, cury);
+ cellidx++;
+
+ /* Generate implicit feedthroughs to satisfy global routing, as */
+ /* many as will fit on the vertical track pitch. */
+
+ feedx = cllx + pitchx[lvert] / 2 + pitchx[lvert];
+ kidx = 1;
+ while (feedx < curx) {
+ fprintf(outfptr, "pin name twfeed%d signal TW_PASS_THRU layer %d %d %d\n",
+ kidx, lvert, feedx, clly);
+ fprintf(outfptr, " equiv name twfeed%d layer %d %d %d\n",
+ kidx, lvert, feedx, cury);
+ feedx += pitchx[lvert];
+ kidx++;
+ }
+
+ /* Write each port and net connection */
+ for (port = inst->portlist; port; port = port->next) {
+
+ /* Any one of these can be a bus: the port, the net, */
+ /* or the instance. */
+
+ int is_port_bus = FALSE;
+ int is_net_bus = FALSE;
+ int is_inst_bus = (arrayidx == -1) ? FALSE : TRUE;
+
+ /* Bus (array) cases:
+ * instance port net
+ * 1) single single single : simple case
+ * 2) single single array : not legal unless bus is 1-bit
+ * 3) single array single : copy net to each port bit
+ * 4) single array array : bit-wise matching net to port
+ * 5) array single single : copy net to each instance
+ * 6) array single array : bit-wise matching net to instance
+ * 7) array array single : copy net in both dimensions
+ * 8) array array array : bit-wise matching net to port, then
+ * copy to each instance.
+ */
+
+ /* Verilog backslash-escaped names have spaces that */
+ /* break pretty much every other format, so replace */
+ /* the space with the (much more sensible) second */
+ /* backslash. This can be detected and changed */
+ /* back by programs converting the syntax back into */
+ /* verilog. */
+
+ netsptr = port->net;
+ if (*port->net == '\\') {
+ netsptr = strchr(port->net, ' ');
+ if (netsptr != NULL) *netsptr = '\\';
+ }
+
+ /* Find the port name in the gate pin list */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ if (!strcmp(port->name, gateginfo->node[j])) break;
+ }
+ if (j == gateginfo->nodes) {
+ /* Is this a bus? */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ char *delim = strrchr(gateginfo->node[j], '[');
+ if (delim != NULL) {
+ *delim = '\0';
+ if (!strcmp(port->name, gateginfo->node[j]))
+ is_port_bus = TRUE;
+ *delim = '[';
+ if (is_port_bus) break;
+ }
+ }
+ }
+
+ /* Check if the net itself is an array */
+ net = HashLookup(port->net, &topcell->nets);
+ if (net && (net->start != -1)) {
+ char *sptr, *dptr, *cptr;
+
+ is_net_bus = TRUE;
+
+ /* However, if net name is a 1-bit bus subnet, then */
+ /* it is not considered to be a bus. Note that */
+ /* brackets inside a verilog backslash-escaped name */
+ /* are not array indicators. */
+
+ dptr = strchr(netsptr, '[');
+ if (dptr) {
+ cptr = strchr(dptr + 1, ':');
+ if (!cptr) {
+ is_net_bus = FALSE;
+ }
+ }
+ }
+
+ if (j == gateginfo->nodes) {
+ fprintf(stderr, "Error: Pin \"%s\" not found in LEF macro \"%s\"!\n",
+ port->name, gateginfo->gatename);
+ result = 1; // Set error result but continue output
+ }
+ else if (is_net_bus == FALSE) {
+ /* Pull pin position from first rectangle in taps list. This */
+ /* does not have to be accurate; just representative. */
+ int bufidx;
+ char *sigptr;
+ DSEG tap = gateginfo->taps[j];
+
+ /* If LEF file failed to specify pin geometry, then use cell center */
+ if (tap == NULL) {
+ px = cllx;
+ py = clly;
+ }
+ else {
+ llx = (int)(tap->x1 * (double)units + 0.5);
+ lly = (int)(tap->y1 * (double)units + 0.5);
+ urx = (int)(tap->x2 * (double)units + 0.5);
+ ury = (int)(tap->y2 * (double)units + 0.5);
+ px = cllx + ((llx + urx) / 2);
+ py = clly + ((lly + ury) / 2);
+ }
+
+ if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) &&
+ ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) &&
+ (gateginfo->direction[j] == PORT_CLASS_INPUT)) {
+ fprintf(outfptr, "pin_group\n");
+ *sigptr = '\0';
+ fprintf(outfptr, "pin name %s_bF$pin/%s ",
+ port->net, port->name);
+ *sigptr = '_';
+ fprintf(outfptr, "signal %s layer %d %d %d\n",
+ port->net, lvert, px, py);
+ fprintf(outfptr, "end_pin_group\n");
+ }
+ else {
+ fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n",
+ port->name, port->net, lvert, px, py);
+ }
+ }
+ else { /* Handle arrays */
+ char *apin, *anet, *dptr, *cptr;
+ int a, pidx, armax, armin;
+
+ if ((is_inst_bus == TRUE) && (is_port_bus == FALSE) &&
+ (is_net_bus == TRUE)) {
+ armax = armin = arrayidx;
+ }
+ else {
+ armax = armin = 0;
+ for (j = 0; j < gateginfo->nodes; j++) {
+ char *delim, *sptr;
+
+ sptr = gateginfo->node[j];
+ if (*sptr == '\\') sptr = strchr(sptr, ' ');
+ if (sptr == NULL) sptr = gateginfo->node[j];
+ delim = strrchr(sptr, '[');
+ if (delim != NULL) {
+ *delim = '\0';
+ if (!strcmp(port->name, gateginfo->node[j])) {
+ if (sscanf(delim + 1, "%d", &pidx) == 1) {
+ if (pidx > armax) armax = pidx;
+ if (pidx < armin) armin = pidx;
+ }
+ }
+ *delim = '[';
+ }
+ }
+ }
+
+ /* To do: Need to check if array is high-to-low or low-to-high */
+ /* Presently assuming arrays are always defined high-to-low */
+
+ apin = (char *)malloc(strlen(port->name) + 15);
+ for (a = armax; a >= armin; a--) {
+ if (is_port_bus)
+ sprintf(apin, "%s[%d]", port->name, a);
+ else
+ sprintf(apin, "%s", port->name);
+
+ /* If net is not delimited by {...} then it is also */
+ /* an array. Otherwise, find the nth element in */
+ /* the brace-enclosed set. */
+
+ /* To do: if any component of the array is a vector */
+ /* then we need to count bits in that vector. */
+
+ if (*port->net == '{') {
+ int aidx;
+ char *sptr, ssave;
+ char *pptr = port->net + 1;
+ for (aidx = 0; aidx < (armax - a); aidx++) {
+ sptr = pptr;
+ while (*sptr != ',' && *sptr != '}') sptr++;
+ pptr = sptr + 1;
+ }
+ sptr = pptr;
+ if (*sptr != '\0') {
+ while (*sptr != ',' && *sptr != '}') sptr++;
+ ssave = *sptr;
+ *sptr = '\0';
+ anet = (char *)malloc(strlen(pptr) + 1);
+ sprintf(anet, "%s", pptr);
+ *sptr = ssave;
+ }
+ else {
+ anet = NULL; /* Must handle this error! */
+ }
+ }
+ else if (((dptr = strrchr(netsptr, '[')) != NULL) &&
+ ((cptr = strrchr(netsptr, ':')) != NULL)) {
+ int fhigh, flow, fidx;
+ sscanf(dptr + 1, "%d", &fhigh);
+ sscanf(cptr + 1, "%d", &flow);
+ if (fhigh > flow) fidx = fhigh - (armax - a);
+ else fidx = flow + (armax - a);
+ anet = (char *)malloc(strlen(port->net) + 15);
+ *dptr = '\0';
+ sprintf(anet, "%s[%d]", port->net, fidx);
+ *dptr = '[';
+ }
+ else {
+ anet = (char *)malloc(strlen(port->net) + 15);
+ sprintf(anet, "%s[%d]", port->net, a);
+ }
+
+ /* Find the corresponding port bit */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ if (anet == NULL) break;
+ if (!strcmp(apin, gateginfo->node[j])) {
+
+ /* Pull pin position from first rectangle in taps */
+ /* list. This does not have to be accurate; just */
+ /* representative. */
+ int bufidx;
+ char *sigptr;
+ DSEG tap = gateginfo->taps[j];
+
+ /* If LEF file failed to specify pin geometry, then */
+ /* use cell center */
+ if (tap == NULL) {
+ px = cllx;
+ py = clly;
+ }
+ else {
+ llx = (int)(tap->x1 * (double)units + 0.5);
+ lly = (int)(tap->y1 * (double)units + 0.5);
+ urx = (int)(tap->x2 * (double)units + 0.5);
+ ury = (int)(tap->y2 * (double)units + 0.5);
+ px = cllx + ((llx + urx) / 2);
+ py = clly + ((lly + ury) / 2);
+ }
+
+ if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) &&
+ ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) &&
+ (gateginfo->direction[j] == PORT_CLASS_INPUT)) {
+ fprintf(outfptr, "pin_group\n");
+ *sigptr = '\0';
+ fprintf(outfptr, "pin name %s_bF$pin/%s ",
+ anet, apin);
+ *sigptr = '_';
+ fprintf(outfptr, "signal %s layer %d %d %d\n",
+ anet, lvert, px, py);
+ fprintf(outfptr, "end_pin_group\n");
+ }
+ else {
+ fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n",
+ apin, anet, lvert, px, py);
+ }
+ break;
+ }
+ }
+ free(anet);
+ if (j == gateginfo->nodes) {
+ fprintf(stderr, "Error: Failed to find port %s in cell %s"
+ " port list!\n", port->name, inst->cellname);
+ }
+ }
+ free(apin);
+ }
+ }
+ if (inst->arraystart < inst->arrayend) {
+ if (++arrayidx > inst->arrayend) break;
+ }
+ else {
+ if (--arrayidx < inst->arrayend) break;
+ }
+ }
+ }
+
+ /* Compute size of a pin as the route pitch (px and py are half sizes). */
+ /* This prevents pins from being spaced tighter than the route pitches. */
+
+ px = pitchx[lvert] >> 1;
+ py = pitchy[lvert] >> 1;
+
+ /* Various attempts to get a valid value for the Y pitch. */
+ if (py == 0) py = pitchy[lvert - 1];
+ if (py == 0) py = pitchy[lvert + 1];
+ if (py == 0) py = pitchx[lvert];
+
+ /* Output pins from the verilog input/output list */
+
+ kidx = 1;
+ for (port = topcell->portlist; port; port = port->next) {
+ if (port->name == NULL) continue;
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ if (net->start > net->end) {
+ for (i = net->start; i >= net->end; i--) {
+ fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx,
+ port->name, i);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n",
+ port->name, i, port->name, i, lvert);
+ kidx++;
+ }
+ }
+ else {
+ for (i = net->start; i <= net->end; i++) {
+ fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx,
+ port->name, i);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n",
+ port->name, i, port->name, i, lvert);
+ kidx++;
+ }
+ }
+ }
+ else {
+ fprintf(outfptr, "pad %d name twpin_%s\n", kidx, port->name);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s signal %s layer %d 0 0\n",
+ port->name, port->name, lvert);
+ kidx++;
+ }
+ fprintf(outfptr, "\n");
+
+ }
+ if (outfile != NULL) fclose(outfptr);
+
+ fflush(stdout);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "vlog2Cel [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "vlog2Cel converts a netlist in verilog to a .cel file\n");
+ fprintf(outf, "for the GrayWolf placement tool. Output on stdout. LEF\n");
+ fprintf(outf, "files are required for determining cell dimensions.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -h Print this message\n");
+ fprintf(outf, " -o <path> Output filename is <path>, otherwise output"
+ " on stdout.\n");
+ fprintf(outf, " -u <value> Use scale <value> for units (default "
+ "100 = centimicrons)\n");
+ fprintf(outf, " -l <path> Read LEF file from <path> (may be called multiple"
+ " times)\n");
+
+} /* helpmessage() */
+
diff --git a/src/vlog2Def.c b/src/vlog2Def.c
new file mode 100644
index 0000000..57fc96a
--- /dev/null
+++ b/src/vlog2Def.c
@@ -0,0 +1,480 @@
+//----------------------------------------------------------------
+// vlog2Def
+//----------------------------------------------------------------
+// Convert from verilog netlist to a pre-placement DEF file
+//----------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* for getopt() */
+#include <math.h> /* For sqrt() and ceil() */
+
+#include "hash.h"
+#include "readverilog.h"
+#include "readlef.h"
+
+int write_output(struct cellrec *, int hasmacros, float aspect, float density,
+ int units, GATE coresite, char *outname);
+void helpmessage(FILE *outf);
+
+/* Linked list for nets */
+
+typedef struct _linkedNet *linkedNetPtr;
+
+typedef struct _linkedNet {
+ char *instname;
+ char *pinname;
+ linkedNetPtr next;
+} linkedNet;
+
+/* Hash table of LEF macros */
+struct hashtable LEFhash;
+
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, result = 0, hasmacros = FALSE;
+ int units = 100;
+ float aspect = 1.0;
+ float density = 1.0;
+ struct cellrec *topcell;
+ GATE coresite = NULL;
+ char *defoutname = NULL;
+
+ InitializeHashTable(&LEFhash, SMALLHASHSIZE);
+
+ while ((i = getopt(argc, argv, "hHl:a:d:u:o:")) != EOF) {
+ switch (i) {
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ return 0;
+ case 'l':
+ result = LefRead(optarg); /* Can be called multiple times */
+ if (result == 0) {
+ helpmessage(stderr);
+ return 1;
+ }
+ break;
+ case 'o':
+ defoutname = strdup(optarg);
+ break;
+ case 'a':
+ if (sscanf(optarg, "%f", &aspect) != 1) {
+ fprintf(stderr, "Could not read aspect value from \"-a %s\"\n",
+ optarg);
+ helpmessage(stderr);
+ return 1;
+ }
+ break;
+ case 'd':
+ if (sscanf(optarg, "%f", &density) != 1) {
+ fprintf(stderr, "Could not read density value from \"-d %s\"\n",
+ optarg);
+ helpmessage(stderr);
+ return 1;
+ }
+ if (density < 0.0 || density > 1.0) {
+ fprintf(stderr, "Illegal density value \"-d %s\"\n", optarg);
+ helpmessage(stderr);
+ return 1;
+ }
+ break;
+ case 'u':
+ if (sscanf(optarg, "%d", &units) != 1) {
+ fprintf(stderr, "Could not read units value from \"-u %s\"\n",
+ optarg);
+ helpmessage(stderr);
+ return 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Bad option switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind >= argc) {
+ fprintf(stderr, "Couldn't find a filename as input\n");
+ helpmessage(stderr);
+ return 1;
+ }
+
+ /* If any LEF files were read, hash the GateInfo list */
+ if (GateInfo != NULL) {
+ GATE gate;
+ for (gate = GateInfo; gate; gate = gate->next) {
+ HashPtrInstall(gate->gatename, gate, &LEFhash);
+ if (!strncmp(gate->gatename, "site_", 5))
+ if (gate->gateclass == MACRO_CLASS_CORE)
+ coresite = gate;
+ }
+ hasmacros = TRUE;
+ }
+
+ topcell = ReadVerilog(argv[optind]);
+ result = write_output(topcell, hasmacros, aspect, density,
+ units, coresite, defoutname);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* output_nets: */
+/* Recursion callback function for each item in Nodehash */
+/*--------------------------------------------------------------*/
+
+struct nlist *output_nets(struct hashlist *p, void *cptr)
+{
+ struct netrec *net;
+ char *sptr = NULL;
+ FILE *outf = (FILE *)cptr;
+ linkedNetPtr nlink, nsrch;
+
+ nlink = (linkedNetPtr)(p->ptr);
+
+ // Verilog backslash-escaped names are decidedly not SPICE
+ // compatible, so replace the mandatory trailing space character
+ // with another backslash.
+
+ if (*p->name == '\\') {
+ sptr = strchr(p->name, ' ');
+ if (sptr != NULL) *sptr = '\\';
+ }
+
+ fprintf(outf, "- %s\n", p->name);
+
+ for (nsrch = nlink; nsrch; nsrch = nsrch->next) {
+ fprintf(outf, " ( %s %s )", nsrch->instname, nsrch->pinname);
+ if (nsrch->next == NULL)
+ fprintf(outf, " ;");
+ fprintf(outf, "\n");
+ }
+
+ if (sptr != NULL) *sptr = ' ';
+ return NULL;
+}
+
+/*--------------------------------------------------------------*/
+/* port_output_specs */
+/* */
+/* Write information to an entry in the DEF PINS section */
+/*--------------------------------------------------------------*/
+
+void port_output_specs(FILE *outfptr, struct portrec *port,
+ LefList *routelayer, int units)
+{
+ char *layername;
+ int x, y, ll, ur, w, hw;
+ LefList pinlayer;
+
+ /* This string array much match the port definitions in readverilog.h */
+ static char *portdirs[] = {"", "INPUT", "OUTPUT", "INOUT"};
+
+ x = y = 0; /* To be done (need constraints?) */
+
+ /* To be done: Layer depends on position and orientation */
+ pinlayer = routelayer[2];
+
+ layername = pinlayer->lefName;
+ w = (int)(0.5 + pinlayer->info.route.width * (float)units);
+ hw = w >> 1;
+ ll = -hw;
+ ur = w - hw;
+
+ if (port->direction > PORT_NONE)
+ fprintf(outfptr, "\n + DIRECTION %s", portdirs[port->direction]);
+ fprintf(outfptr, "\n + LAYER %s ( %d %d ) ( %d %d )", layername,
+ ll, ll, ur, ur);
+ fprintf(outfptr, "\n + PLACED ( %d %d ) N ;\n", x, y);
+}
+
+/*--------------------------------------------------------------*/
+/* write_output */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int write_output(struct cellrec *topcell, int hasmacros, float aspect,
+ float density, int units, GATE coresite, char *outname)
+{
+ FILE *outfptr = stdout;
+ int ncomp, npin, nnet, start, end, i, result = 0;
+ int totalwidth, totalheight, rowwidth, rowheight, numrows;
+ int sitewidth, siteheight, numsites;
+ char portnet[512];
+
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+
+ struct hashtable Nodehash;
+
+ /* Static string "PIN" for ports */
+ static char pinname[] = "PIN";
+
+ linkedNetPtr nlink, nsrch;
+ LefList slef;
+ LefList routelayer[3];
+
+ /* Open the output file (unless name is NULL, in which case use stdout) */
+ if (outname != NULL) {
+ outfptr = fopen(outname, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Cannot open file %s for writing.\n", outname);
+ return 1;
+ }
+ }
+
+ /* Hash the nets */
+
+ InitializeHashTable(&Nodehash, LARGEHASHSIZE);
+ nnet = 0;
+ for (port = topcell->portlist; port; port = port->next) {
+ if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
+ start = net->start;
+ end = net->end;
+ }
+ else start = end = -1;
+
+ if (start > end) {
+ int tmp;
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+ for (i = start; i <= end; i++) {
+ nlink = (linkedNetPtr)malloc(sizeof(linkedNet));
+ nlink->instname = pinname;
+
+ if (start == -1)
+ nlink->pinname = port->name;
+ else {
+ sprintf(portnet, "%s[%d]", port->name, i);
+ nlink->pinname = strdup(portnet);
+ }
+ nlink->next = NULL;
+ if ((nsrch = HashLookup(nlink->pinname, &Nodehash)) != NULL) {
+ while (nsrch->next) nsrch = nsrch->next;
+ nsrch->next = nlink;
+ }
+ else {
+ HashPtrInstall(nlink->pinname, nlink, &Nodehash);
+ nnet++;
+ }
+ }
+ }
+ totalwidth = 0;
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ for (port = inst->portlist; port; port = port->next) {
+
+ nlink = (linkedNetPtr)malloc(sizeof(linkedNet));
+ nlink->instname = inst->instname;
+ nlink->pinname = port->name;
+ nlink->next = NULL;
+
+ if ((nsrch = HashLookup(port->net, &Nodehash)) != NULL) {
+ while (nsrch->next) nsrch = nsrch->next;
+ nsrch->next = nlink;
+ }
+ else {
+ HashPtrInstall(port->net, nlink, &Nodehash);
+ nnet++;
+ }
+ }
+
+ if (hasmacros) {
+ GATE gate;
+
+ gate = HashLookup(inst->cellname, &LEFhash);
+ if (gate) {
+ /* Make sure this is a core cell */
+ if (gate->gateclass == MACRO_CLASS_CORE) {
+ totalwidth += (int)(gate->width * (float)units);
+ rowheight = (int)(gate->height * (float)units);
+ }
+ /* To do: Handle non-core cell records */
+ /* (specifically PAD and BLOCK). */
+ }
+ }
+ }
+
+ /* For pin placement, find the 2nd and 3rd route layer LEF names. */
+ /* NOTE: This only ensures that the output is valid LEF; it does */
+ /* not do anything about applying pin constraints. */
+
+ for (i = 0; i < 3; i++)
+ routelayer[i] = (LefList)NULL;
+
+ for (slef = LefInfo; slef; slef = slef->next)
+ if (slef->lefClass == CLASS_ROUTE)
+ if ((slef->type < 3) && (slef->type >= 0))
+ routelayer[slef->type] = slef;
+
+ /* Write output DEF header */
+ fprintf(outfptr, "VERSION 5.6 ;\n");
+ /* fprintf(outfptr, "NAMESCASESENSITIVE ON ;\n"); */
+ fprintf(outfptr, "DIVIDERCHAR \"/\" ;\n");
+ fprintf(outfptr, "BUSBITCHARS \"[]\" ;\n");
+ fprintf(outfptr, "DESIGN %s ;\n", topcell->name);
+ fprintf(outfptr, "UNITS DISTANCE MICRONS %d ;\n", units);
+ fprintf(outfptr, "\n");
+
+ /* Calculate pre-placement die area, rows, and tracks, and output the same, */
+ /* depending on what has been read in from LEF files. */
+
+ if (hasmacros) {
+ /* NOTE: Use a prorated density that is slightly lower than the target */
+ /* or else the placement can fail due to fewer sites available then */
+ /* cell area to place, after accounting for density. */
+
+ int efftotalwidth = (int)ceilf((float)totalwidth / (density * 0.95));
+
+ numrows = (int)ceilf(sqrtf(efftotalwidth / (aspect * rowheight)));
+ rowwidth = (int)ceilf(efftotalwidth / numrows);
+ totalheight = (int)ceilf(rowheight * numrows);
+ sitewidth = (int)ceilf(coresite->width * units);
+ siteheight = (int)ceilf(coresite->height * units);
+
+ /* Diagnostic */
+ fprintf(stdout, "Diagnostic:\n");
+ fprintf(stdout, "Total width of all cells = %gum\n", (float)totalwidth / (float)units);
+ fprintf(stdout, "Effective total width after density planning = %gum\n", (float)efftotalwidth / (float)units);
+ fprintf(stdout, "Site size = (%gum, %gum)\n", (float)sitewidth / (float)units, siteheight / (float)units);
+ fprintf(stdout, "Row height = %gum\n", (float)rowheight / (float)units);
+ fprintf(stdout, "Row width = %gum\n", (float)rowwidth / (float)units);
+ fprintf(stdout, "Total height = %gum\n", (float)totalheight / (float)units);
+
+ /* To do: compute additional area for pins */
+
+ fprintf(outfptr, "DIEAREA ( 0 0 ) ( %d %d ) ;\n", rowwidth, totalheight);
+ fprintf(outfptr, "\n");
+
+ /* Compute site placement and generate ROW statements */
+
+ numsites = (int)ceilf((float)rowwidth / (float)sitewidth);
+ for (i = 0; i < numrows; i++) {
+ fprintf(outfptr, "ROW ROW_%d %s 0 %d %c DO %d BY 1 STEP %d 0 ;\n",
+ i + 1, coresite->gatename + 5, i * siteheight,
+ ((i % 2) == 0) ? 'N' : 'S', numsites, sitewidth);
+ }
+ fprintf(outfptr, "\n");
+ }
+
+ /* Write components in the order of the input file */
+
+ ncomp = 0;
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ ncomp++;
+ if (inst->arraystart != -1) {
+ int arrayw = inst->arraystart - inst->arrayend;
+ ncomp += (arrayw < 0) ? -arrayw : arrayw;
+ }
+ }
+ fprintf(outfptr, "COMPONENTS %d ;\n", ncomp);
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ if (inst->arraystart != -1) {
+ int ahigh, alow, j;
+ if (inst->arraystart > inst->arrayend) {
+ ahigh = inst->arraystart;
+ alow = inst->arrayend;
+ }
+ else {
+ ahigh = inst->arrayend;
+ alow = inst->arraystart;
+ }
+ for (j = ahigh; j >= alow; j--) {
+ fprintf(outfptr, "- %s[%d] %s ;\n", inst->instname, j, inst->cellname);
+ }
+ }
+ else
+ fprintf(outfptr, "- %s %s ;\n", inst->instname, inst->cellname);
+ }
+ fprintf(outfptr, "END COMPONENTS\n\n");
+
+ npin = 0;
+ for (port = topcell->portlist; port; port = port->next) {
+ if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
+ int btot = net->start - net->end;
+ if (btot < 0) btot = -btot;
+ npin += btot + 1;
+ }
+ else
+ npin++;
+ }
+ fprintf(outfptr, "PINS %d ;\n", npin);
+
+ for (port = topcell->portlist; port; port = port->next) {
+ if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
+ if (net->start == -1) {
+ fprintf(outfptr, "- %s + NET %s", port->name, port->name);
+ port_output_specs(outfptr, port, routelayer, units);
+ }
+ else if (net->start > net->end) {
+ for (i = net->start; i >= net->end; i--) {
+ fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i,
+ port->name, i);
+ port_output_specs(outfptr, port, routelayer, units);
+ }
+ }
+ else {
+ for (i = net->start; i <= net->end; i++) {
+ fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i,
+ port->name, i);
+ port_output_specs(outfptr, port, routelayer, units);
+ }
+ }
+ }
+ else {
+ fprintf(outfptr, "- %s + NET %s", port->name, port->name);
+ port_output_specs(outfptr, port, routelayer, units);
+ }
+ }
+
+ fprintf(outfptr, "END PINS\n\n");
+
+ fprintf(outfptr, "NETS %d ;\n", nnet);
+ RecurseHashTablePointer(&Nodehash, output_nets, outfptr);
+ fprintf(outfptr, "END NETS\n\n");
+
+ /* End the design */
+ fprintf(outfptr, "END DESIGN\n");
+
+ if (outname != NULL) fclose(outfptr);
+
+ fflush(stdout);
+ return result;
+
+} /* write_output */
+
+/*--------------------------------------------------------------*/
+/* C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf,"vlog2Def <netlist>\n");
+ fprintf(outf,"\n");
+ fprintf(outf,"vlog2Def converts a verilog netlist to a pre-placement DEF file.\n");
+ fprintf(outf,"\n");
+ fprintf(outf,"options:\n");
+ fprintf(outf,"\n");
+ fprintf(outf," -h Print this message\n");
+ fprintf(outf," -o <path> Set output filename (otherwise output is on stdout).\n");
+ fprintf(outf," -l <path> Read LEF file from <path> (may be called multiple"
+ " times)\n");
+ fprintf(outf," -a <value> Set aspect ratio to <value> (default 1.0)\n");
+ fprintf(outf," -d <value> Set density to <value> (default 1.0)\n");
+ fprintf(outf," -u <value> Set units-per-micron to <value) (default 100)\n");
+
+} /* helpmessage() */
+
diff --git a/src/vlog2Spice.c b/src/vlog2Spice.c
new file mode 100644
index 0000000..5b70297
--- /dev/null
+++ b/src/vlog2Spice.c
@@ -0,0 +1,636 @@
+//--------------------------------------------------------------
+// vlog2Spice
+//
+// Convert a structural verilog netlist (with power and ground
+// nets!) into a SPICE netlist.
+//
+// Revision 0, 2006-11-11: First release by R. Timothy Edwards.
+// Revision 1, 2009-07-13: Minor cleanups by Philipp Klaus Krause.
+// Revision 2, 2013-05-10: Modified to take a library of subcell
+// definitions to use for determining port order.
+// Revision 3, 2013-10-09: Changed from BDnet2BSpice to
+// blif2BSpice
+// Revision 4, 2018-11-28: Changed from blif2BSpice to vlog2Spice
+//
+//--------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readverilog.h"
+
+#define LengthOfLine 16384
+
+#define DO_INCLUDE 0x01
+#define DO_DELIMITER 0x02
+
+/* Linked list of names of SPICE libraries to read */
+typedef struct _linkedstring *LinkedStringPtr;
+
+typedef struct _linkedstring {
+ char *string;
+ LinkedStringPtr next;
+} LinkedString;
+
+/* Function prototypes */
+int write_output(struct cellrec *, LinkedStringPtr, char *, int);
+int loc_getline(char s[], int lim, FILE *fp);
+void helpmessage(FILE *);
+
+//--------------------------------------------------------
+
+int main (int argc, char *argv[])
+{
+ int i, result = 0;
+ int flags = 0;
+ char *eptr;
+
+ char *vloginname = NULL;
+ char *spclibname = NULL;
+ char *spcoutname = NULL;
+
+ LinkedStringPtr spicelibs = NULL, newspicelib;
+
+
+ struct cellrec *topcell = NULL;
+
+ while ((i = getopt(argc, argv, "hHidD:l:s:o:")) != EOF) {
+ switch (i) {
+ case 'l':
+ newspicelib = (LinkedStringPtr)malloc(sizeof(LinkedString));
+ newspicelib->string = strdup(optarg);
+ newspicelib->next = spicelibs;
+ spicelibs = newspicelib;
+ break;
+ case 'o':
+ spcoutname = strdup(optarg);
+ break;
+ case 'i':
+ flags |= DO_INCLUDE;
+ break;
+ case 'd':
+ flags |= DO_DELIMITER;
+ break;
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ exit(0);
+ break;
+ case 'D':
+ eptr = strchr(optarg, '=');
+ if (eptr != NULL) {
+ *eptr = '\0';
+ VerilogDefine(optarg, eptr + 1);
+ *eptr = '=';
+ }
+ else
+ VerilogDefine(optarg, "1");
+ break;
+ default:
+ fprintf(stderr, "Unknown switch %c\n", (char)i);
+ helpmessage(stderr);
+ exit(1);
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ vloginname = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename as input\n");
+ helpmessage(stderr);
+ exit(1);
+ }
+ optind++;
+
+ topcell = ReadVerilog(vloginname);
+ if (topcell != NULL)
+ result = write_output(topcell, spicelibs, spcoutname, flags);
+ else
+ result = 1; /* Return error code */
+
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* Verilog backslash notation, which is the most absurd syntax */
+/* in the known universe, is fundamentally incompatible with */
+/* SPICE. The ad-hoc solution used by qflow is to replace */
+/* the trailing space with another backslash such that the */
+/* name is SPICE-compatible and the original syntax can be */
+/* recovered when needed. */
+/*--------------------------------------------------------------*/
+
+void backslash_fix(char *netname)
+{
+ char *sptr;
+
+ if (*netname == '\\')
+ if ((sptr = strchr(netname, ' ')) != NULL)
+ *sptr = '\\';
+}
+
+/*--------------------------------------------------------------*/
+/* write_output --- Write the SPICE netlist output */
+/* */
+/* ARGS: */
+/* RETURNS: 0 on success, 1 on error. */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int write_output(struct cellrec *topcell, LinkedStringPtr spicelibs,
+ char *outname, int flags)
+{
+ FILE *libfile;
+ FILE *outfile;
+ char *libname;
+ LinkedStringPtr curspicelib;
+
+ struct netrec *net;
+ struct instance *inst;
+ struct portrec *port;
+ struct portrec *newport, *portlist, *lastport;
+
+ int i, j, k, start, end, pcount = 1;
+ int result = 0;
+ int instidx, insti;
+
+ char *lptr;
+ char *sp, *sp2;
+ char line[LengthOfLine];
+
+ struct hashtable Libhash;
+
+ if (outname != NULL) {
+ outfile = fopen(outname, "w");
+ if (outfile == NULL) {
+ fprintf(stderr, "Error: Couldn't open file %s for writing\n", outname);
+ return 1;
+ }
+ }
+ else
+ outfile = stdout;
+
+ /* Initialize SPICE library hash table */
+ InitializeHashTable(&Libhash, SMALLHASHSIZE);
+
+ // Read one or more SPICE libraries of subcircuits and use them to define
+ // the order of pins that were read from LEF (which is not necessarily in
+ // SPICE pin order).
+
+ for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) {
+ libname = curspicelib->string;
+
+ libfile = fopen(libname, "r");
+ if (libfile == NULL) {
+ fprintf(stderr, "Couldn't open %s for reading\n", libname);
+ continue;
+ }
+
+ /* Read SPICE library of subcircuits, if one is specified. */
+ /* Retain the name and order of ports passed to each */
+ /* subcircuit. */
+
+ j = 0;
+ while (loc_getline(line, sizeof(line), libfile) > 0) {
+ if (!strncasecmp(line, ".subckt", 7)) {
+ char *cellname;
+ lastport = NULL;
+ portlist = NULL;
+
+ /* Read cellname */
+ sp = line + 7;
+ while (isspace(*sp) && (*sp != '\n')) sp++;
+ sp2 = sp;
+ while (!isspace(*sp2) && (*sp2 != '\n')) sp2++;
+ *sp2 = '\0';
+
+ /* Keep a record of the cellname until we generate the */
+ /* hash entry for it */
+ cellname = strdup(sp);
+
+ /* Now fill out the ordered port list */
+ sp = sp2 + 1;
+ while (isspace(*sp) && (*sp != '\n') && (*sp != '\0')) sp++;
+ while (sp) {
+
+ /* Move string pointer to next port name */
+
+ if (*sp == '\n' || *sp == '\0') {
+ loc_getline(line, sizeof(line), libfile);
+ if (*line == '+')
+ sp = line + 1;
+ else
+ break;
+ }
+ while (isspace(*sp) && (*sp != '\n')) sp++;
+
+ /* Terminate port name and advance pointer */
+ sp2 = sp;
+ while (!isspace(*sp2) && (*sp2 != '\n') && (*sp2 != '\0')) sp2++;
+ *sp2 = '\0';
+
+ /* Add port to list (in forward order) */
+
+ newport = (struct portrec *)malloc(sizeof(struct portrec));
+ if (portlist == NULL)
+ portlist = newport;
+ else
+ lastport->next = newport;
+ lastport = newport;
+ newport->name = strdup(sp);
+ newport->net = NULL;
+ newport->direction = 0;
+ newport->next = NULL;
+
+ sp = sp2 + 1;
+ }
+
+ /* Read input to end of subcircuit */
+
+ if (strncasecmp(line, ".ends", 4)) {
+ while (loc_getline(line, sizeof(line), libfile) > 0)
+ if (!strncasecmp(line, ".ends", 4))
+ break;
+ }
+
+ /* Hash the new port record by cellname */
+ HashPtrInstall(cellname, portlist, &Libhash);
+ free(cellname);
+ }
+ }
+ fclose(libfile);
+ }
+
+ /* Write output header */
+ fprintf(outfile, "*SPICE netlist created from verilog structural netlist module "
+ "%s by vlog2Spice (qflow)\n", topcell->name);
+ fprintf(outfile, "*This file may contain array delimiters, not for use in simulation.\n");
+ fprintf(outfile, "\n");
+
+ /* If flags has DO_INCLUDE then dump the contents of the */
+ /* libraries. If 0, then just write a .include line. */
+
+ for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) {
+ libname = curspicelib->string;
+
+ if (flags & DO_INCLUDE) {
+ libfile = fopen(libname, "r");
+ if (libname != NULL) {
+ fprintf(outfile, "** Start of included library %s\n", libname);
+ /* Write out the subcircuit library file verbatim */
+ while (loc_getline(line, sizeof(line), libfile) > 0)
+ fputs(line, outfile);
+ fprintf(outfile, "** End of included library %s\n", libname);
+ fclose(libfile);
+ }
+ }
+ else {
+ fprintf(outfile, ".include %s\n", libname);
+ }
+ }
+ fprintf(outfile, "\n");
+
+ /* Generate the subcircuit definition, adding power and ground nets */
+
+ fprintf(outfile, ".subckt %s ", topcell->name);
+
+ for (port = topcell->portlist; port; port = port->next) {
+ if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
+ start = net->start;
+ end = net->end;
+ }
+ else start = end = -1;
+
+ if (start > end) {
+ int tmp;
+ tmp = start;
+ start = end;
+ end = tmp;
+ }
+ if (start == end) {
+ fprintf(outfile, "%s", port->name);
+ if (pcount++ % 8 == 7) {
+ pcount = 0;
+ fprintf(outfile, "\n+");
+ }
+ fprintf(outfile, " ");
+ }
+ else {
+ for (i = start; i <= end; i++) {
+ /* Note that use of brackets is not legal SPICE syntax */
+ /* but suffices for LVS and such. Output should be */
+ /* post-processed before using in simulation. */
+ if (flags & DO_DELIMITER)
+ fprintf(outfile, "%s<%d>", port->name, i);
+ else
+ fprintf(outfile, "%s[%d]", port->name, i);
+
+ if (pcount++ % 8 == 7) {
+ pcount = 0;
+ fprintf(outfile, "\n+");
+ }
+ fprintf(outfile, " ");
+ }
+ }
+ }
+ fprintf(outfile, "\n\n");
+
+ /* Output instances */
+
+ instidx = -1;
+ for (inst = topcell->instlist; inst; ) {
+ int argcnt;
+ struct portrec *libport;
+
+ /* Check if the instance is an array */
+ if (inst->arraystart != -1) {
+ if (instidx == -1) {
+ instidx = inst->arraystart;
+ insti = 0;
+ }
+ else if (inst->arraystart > inst->arrayend) {
+ instidx--;
+ insti++;
+ }
+ else if (inst->arraystart < inst->arrayend) {
+ instidx++;
+ insti++;
+ }
+ }
+
+ if (inst->arraystart == -1)
+ fprintf(outfile, "X%s ", inst->instname);
+ else
+ fprintf(outfile, "X%s[%d] ", inst->instname, instidx);
+ pcount = 1;
+
+ /* Search library records for subcircuit */
+
+ portlist = (struct portrec *)HashLookup(inst->cellname, &Libhash);
+
+ /* If no library entry exists, complain about arbitrary port */
+ /* order, then use the instance's port names to create a port */
+ /* record entry. */
+
+ if (portlist == NULL) {
+ fprintf(stderr, "Warning: No SPICE subcircuit for %s. Pin"
+ " order will be arbitrary.\n", inst->cellname);
+ lastport = portlist = NULL;
+ for (libport = inst->portlist; libport; libport = libport->next) {
+ newport = (struct portrec *)malloc(sizeof(struct portrec));
+ if (portlist == NULL)
+ portlist = newport;
+ else
+ lastport->next = newport;
+ lastport = newport;
+ newport->name = strdup(libport->name);
+ newport->net = NULL;
+ newport->direction = libport->direction;
+ newport->next = NULL;
+ }
+ HashPtrInstall(inst->cellname, portlist, &Libhash);
+ }
+
+ /* Output pin connections in the order of the LEF record, which */
+ /* has been forced to match the port order of the SPICE library */
+ /* If there is no SPICE library record, output in the order of */
+ /* the instance, which may or may not be correct. In such a */
+ /* case, flag a warning. */
+
+ argcnt = 0;
+ for (libport = portlist; ; libport = libport->next) {
+ char *dptr, dsave;
+ int idx = 0, is_array = FALSE, match = FALSE;
+
+ if (portlist != NULL && libport == NULL) break;
+
+ argcnt++;
+ argcnt %= 8;
+ if (argcnt == 7)
+ fprintf(outfile, "\n+ ");
+
+ dptr = NULL;
+ if (libport) {
+ for (dptr = libport->name; *dptr != '\0'; dptr++) {
+ if (*dptr == '[') {
+ is_array = TRUE;
+ dsave = *dptr;
+ *dptr = '\0';
+ sscanf(dptr + 1, "%d", &idx);
+ break;
+ }
+ }
+ }
+
+ /* Treat arrayed instances like a bit-blasted port */
+ if ((is_array == FALSE) && (inst->arraystart != -1)) {
+ is_array = TRUE;
+ idx = insti;
+ }
+
+ for (port = inst->portlist; port; port = port->next) {
+ /* Find the port name in the instance which matches */
+ /* the port name in the macro definition. */
+
+ if (libport) {
+ if (!strcasecmp(libport->name, port->name)) {
+ match = TRUE;
+ break;
+ }
+ }
+ else {
+ match = TRUE;
+ break;
+ }
+ }
+ if (!match) {
+ char *gptr;
+
+ /* Deal with annoying use of "!" as a global indicator, */
+ /* which is sometimes used. . . */
+ gptr = libport->name + strlen(libport->name) - 1;
+ if (*gptr == '!') {
+ *gptr = '\0';
+ for (port = inst->portlist; port; port = port->next) {
+ if (!strcasecmp(libport->name, port->name)) {
+ match = TRUE;
+ break;
+ }
+ }
+ *gptr = '!';
+ }
+ else {
+ for (port = inst->portlist; port; port = port->next) {
+ gptr = port->name + strlen(port->name) - 1;
+ if (*gptr == '!') {
+ *gptr = '\0';
+ if (!strcasecmp(libport->name, port->name)) match = TRUE;
+ *gptr = '!';
+ if (match == TRUE) break;
+ }
+ }
+ }
+ }
+ if (!match) {
+ fprintf(stderr, "Error: Instance %s has no port %s!\n",
+ inst->instname, libport->name);
+ }
+ else {
+ if (flags & DO_DELIMITER) {
+ char *d1ptr, *d2ptr;
+ if ((d1ptr = strchr(port->net, '[')) != NULL) {
+ if ((d2ptr = strchr(d1ptr + 1, ']')) != NULL) {
+ *d1ptr = '<';
+ *d2ptr = '>';
+ }
+ }
+ }
+ if (is_array) {
+ char *portname = port->net;
+ if (*portname == '{') {
+ char *epos, ssave;
+ int k;
+
+ /* Bus notation "{a, b, c, ... }" */
+ /* Go to the end and count bits backwards. */
+ /* until reaching the idx'th position. */
+
+ /* To be done: Move GetIndexedNet() from */
+ /* vlog2Verilog.c to readverilog.c and call */
+ /* it from here. It is more complete than */
+ /* this implementation. */
+
+ while (*portname != '}' && *portname != '\0') portname++;
+ for (k = 0; k < idx; k++) {
+ epos = portname;
+ portname--;
+ while (*portname != ',' && portname > port->net)
+ portname--;
+ }
+ if (*portname == ',') portname++;
+ ssave = *epos;
+ *epos = '\0';
+ backslash_fix(portname);
+ fprintf(outfile, "%s", portname);
+ *epos = ssave;
+ }
+ else {
+ struct netrec wb;
+
+ GetBus(portname, &wb, &topcell->nets);
+
+ if (wb.start < 0) {
+ /* portname is not a bus */
+ backslash_fix(portname);
+ fprintf(outfile, "%s", portname);
+ }
+ else {
+ int lidx;
+ if (wb.start < wb.end)
+ lidx = wb.start + idx;
+ else
+ lidx = wb.start - idx;
+ /* portname is a partial or full bus */
+ dptr = strrchr(portname, '[');
+ if (dptr) *dptr = '\0';
+ backslash_fix(portname);
+ if (flags & DO_DELIMITER)
+ fprintf(outfile, "%s<%d>", portname, lidx);
+ else
+ fprintf(outfile, "%s[%d]", portname, lidx);
+ if (dptr) *dptr = '[';
+ }
+ }
+ }
+ else {
+ backslash_fix(port->net);
+ fprintf(outfile, "%s", port->net);
+ }
+
+ if (pcount++ % 8 == 7) {
+ pcount = 0;
+ fprintf(outfile, "\n+");
+ }
+ fprintf(outfile, " ");
+ }
+
+ if (portlist == NULL) {
+ fprintf(stdout, "Warning: No defined subcircuit %s for "
+ "instance %s!\n", inst->cellname, inst->instname);
+ fprintf(stdout, "Pins will be output in arbitrary order.\n");
+ break;
+ }
+ if (dptr != NULL) *dptr = '[';
+ }
+ fprintf(outfile, "%s\n", inst->cellname);
+
+ if ((inst->arraystart != -1) && (instidx != inst->arrayend)) continue;
+ instidx = -1;
+ inst = inst->next;
+ }
+ fprintf(outfile, "\n.ends\n");
+ fprintf(outfile, ".end\n");
+
+ if (outname != NULL) fclose(outfile);
+
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/*C loc_getline: read a line, return length */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int loc_getline(char *s, int lim, FILE *fp)
+{
+ int c, i;
+
+ i = 0;
+ while(--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
+ s[i++] = c;
+ if (c == '\n')
+ s[i++] = c;
+ s[i] = '\0';
+ if (c == EOF) i = 0;
+ return i;
+}
+
+/*--------------------------------------------------------------*/
+/*C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: error code (0 = success, 1 = error) */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *fout)
+{
+ fprintf(fout, "vlog2Spice [-options] netlist \n");
+ fprintf(fout, "\n");
+ fprintf(fout, "vlog2Spice converts a netlist in verilog format \n");
+ fprintf(fout, "to Spice subcircuit format. Output on stdout unless -o option used.\n");
+ fprintf(fout, "Input file must be a structural verilog netlist with power and ground.\n");
+ fprintf(fout, "\n");
+ fprintf(fout, "Options:\n");
+ fprintf(fout, " -h Print this message\n");
+ fprintf(fout, " -i Generate include statement for library, not a dump.\n");
+ fprintf(fout, " -d Convert array delimiter brackets to angle brackets.\n");
+ fprintf(fout, " -D <key>=<value> Preregister a verilog definition.\n");
+ fprintf(fout, " -l <path> Specify path to SPICE library of standard cells.\n");
+ fprintf(fout, " -o <path> Specify path to output SPICE file.\n");
+ fprintf(fout, "\n");
+
+} /* helpmessage() */
+
diff --git a/src/vlog2Verilog.c b/src/vlog2Verilog.c
new file mode 100644
index 0000000..1b0ebf8
--- /dev/null
+++ b/src/vlog2Verilog.c
@@ -0,0 +1,1018 @@
+//----------------------------------------------------------------
+// vlog2Verilog
+//----------------------------------------------------------------
+// Convert between verilog styles.
+// Options include bit-blasting vectors and adding power
+// supply connections.
+//
+// Revision 0, 2018-11-29: First release by R. Timothy Edwards.
+//
+// This program is written in ISO C99.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* For getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readverilog.h"
+#include "readlef.h"
+
+int write_output(struct cellrec *, unsigned char, char *);
+void helpmessage(FILE *outf);
+void cleanup_string(char *);
+int is_pwr_name(char *);
+
+char *VddNet = NULL;
+char *GndNet = NULL;
+char *AntennaCell = NULL;
+
+struct hashtable Lefhash;
+
+/* Define option flags */
+
+#define IMPLICIT_POWER (unsigned char)0x01
+#define MAINTAIN_CASE (unsigned char)0x02
+#define BIT_BLAST (unsigned char)0x04
+#define NONAME_POWER (unsigned char)0x08
+#define ADD_ANTENNA (unsigned char)0x10
+
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, result;
+ unsigned char Flags;
+
+ char *vloginname = NULL;
+ char *vlogoutname = NULL;
+ struct cellrec *topcell;
+
+ Flags = (unsigned char)IMPLICIT_POWER;
+
+ VddNet = strdup("VDD");
+ GndNet = strdup("VSS");
+
+ InitializeHashTable(&Lefhash, SMALLHASHSIZE);
+
+ while ((i = getopt(argc, argv, "pbchnHv:g:l:o:a:")) != EOF) {
+ switch( i ) {
+ case 'p':
+ Flags &= ~IMPLICIT_POWER;
+ break;
+ case 'b':
+ Flags |= BIT_BLAST;
+ break;
+ case 'c':
+ Flags |= MAINTAIN_CASE;
+ break;
+ case 'a':
+ Flags |= ADD_ANTENNA;
+ if (AntennaCell != NULL) free(AntennaCell);
+ AntennaCell = strdup(optarg);
+ break;
+ case 'n':
+ Flags |= NONAME_POWER;
+ break;
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ exit(0);
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'v':
+ free(VddNet);
+ VddNet = strdup(optarg);
+ cleanup_string(VddNet);
+ break;
+ case 'o':
+ vlogoutname = strdup(optarg);
+ break;
+ case 'g':
+ free(GndNet);
+ GndNet = strdup(optarg);
+ cleanup_string(GndNet);
+ break;
+ default:
+ fprintf(stderr,"Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ vloginname = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename as input\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ /* If any LEF files were read, hash the GateInfo list */
+ if (GateInfo != NULL) {
+ GATE gate;
+ for (gate = GateInfo; gate; gate = gate->next) {
+ HashPtrInstall(gate->gatename, gate, &Lefhash);
+ }
+ }
+
+ topcell = ReadVerilog(vloginname);
+ result = write_output(topcell, Flags, vlogoutname);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* Name compare for VddNet and GndNet. Remove any trailing "!" */
+/* (global reference) from the net name. */
+/*--------------------------------------------------------------*/
+
+int is_pwr_name(char *text)
+{
+ int n = strlen(text);
+
+ if (*(text + n - 1) == '!') *(text + n - 1) = '\0';
+
+ if (!strcmp(text, VddNet)) return 0;
+ if (!strcmp(text, GndNet)) return 0;
+ return 1;
+}
+
+/*--------------------------------------------------------------*/
+/* String input cleanup (mainly strip quoted text) */
+/*--------------------------------------------------------------*/
+
+void cleanup_string(char *text)
+{
+ int i;
+ char *sptr, *wptr;
+
+ /* Remove quotes from quoted strings */
+
+ sptr = strchr(text, '"');
+ if (sptr != NULL) {
+ i = 0;
+ while (sptr[i + 1] != '"') {
+ sptr[i] = sptr[i + 1];
+ i++;
+ }
+ sptr[i] = '\0';
+ }
+
+ /* Remove any trailing "!" used as a global identifier */
+ if ((sptr > text) && (*(sptr - 1) == '!')) {
+ *(sptr - 1) = '\0';
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec nets hash table */
+/*--------------------------------------------------------------------------*/
+
+struct nlist *output_wires(struct hashlist *p, void *cptr)
+{
+ struct netrec *net;
+ FILE *outf = (FILE *)cptr;
+
+ net = (struct netrec *)(p->ptr);
+
+ /* Ignore the power and ground nets; these have already been output */
+ /* This also extends to any net in the form <digit><single quote> */
+
+ if (p->name[0] == '\'') return NULL;
+ if (isdigit(p->name[0])) {
+ char c, *dptr;
+
+ dptr = p->name;
+ while (isdigit(*dptr)) dptr++;
+ if (*dptr == '\0') return NULL;
+ else if (*dptr == '\'') {
+ c = *(dptr + 1);
+ if (c == 'b' || c == 'h' || c == 'd' || c == 'o')
+ return NULL;
+ }
+ }
+ else if (is_pwr_name(p->name)) return NULL;
+
+ fprintf(outf, "wire ");
+ if (net->start >= 0 && net->end >= 0) {
+ fprintf(outf, "[%d:%d] ", net->start, net->end);
+ }
+ fprintf(outf, "%s ;\n", p->name);
+ return NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* Convert a verilog number into a binary string. The target string */
+/* "bitstring" is assumed to be at least (bits + 1) bytes in length. */
+/* "c" is a conversion type ('h', 'o', or 'd'). */
+/*----------------------------------------------------------------------*/
+/* hexidecimal to binary */
+
+char *hex2binary(char *uval, int bits)
+{
+ int first, dval, hexc;
+ char *bitstring = (char *)malloc(1 + bits);
+ char *hptr, *bptr, *hstring;
+ char binhex[5];
+
+ first = bits % 4;
+ hexc = ((first == 0) ? 0 : 1) + (bits / 4); /* Number of hex characters */
+
+ hstring = (char *)malloc(hexc + 1);
+ strncpy(hstring, uval, hexc);
+ *(hstring + hexc) = '\0';
+ for (hptr = hstring; *hptr != '\0'; hptr++) {
+
+ /* Catch 'x', 'X', etc., and convert to zero. */
+ /* Keep 'z'/'Z' as this must be handled differently */
+
+ if ((*hptr == 'z') || (*hptr == 'Z')) *hptr = 'Z';
+ else if ((*hptr == 'x') || (*hptr == 'X')) *hptr = '0';
+ }
+
+ hptr = hstring;
+ bptr = bitstring;
+ while (*hptr != '\0') {
+ switch(*hptr) {
+ case 'Z':
+ strcpy(binhex, "ZZZZ");
+ break;
+ case '0':
+ strcpy(binhex, "0000");
+ break;
+ case '1':
+ strcpy(binhex, "0001");
+ break;
+ case '2':
+ strcpy(binhex, "0010");
+ break;
+ case '3':
+ strcpy(binhex, "0011");
+ break;
+ case '4':
+ strcpy(binhex, "0100");
+ break;
+ case '5':
+ strcpy(binhex, "0101");
+ break;
+ case '6':
+ strcpy(binhex, "0110");
+ break;
+ case '7':
+ strcpy(binhex, "0111");
+ break;
+ case '8':
+ strcpy(binhex, "1000");
+ break;
+ case '9':
+ strcpy(binhex, "1001");
+ break;
+ case 'a':
+ strcpy(binhex, "1010");
+ break;
+ case 'b':
+ strcpy(binhex, "1011");
+ break;
+ case 'c':
+ strcpy(binhex, "1100");
+ break;
+ case 'd':
+ strcpy(binhex, "1101");
+ break;
+ case 'e':
+ strcpy(binhex, "1110");
+ break;
+ case 'f':
+ strcpy(binhex, "1111");
+ break;
+ }
+ if (first > 0) {
+ strncpy(bptr, binhex + (4 - first), first);
+ *(bptr + first) = '\0';
+ bptr += first;
+ first = 0;
+ }
+ else {
+ strcpy(bptr, binhex);
+ bptr += 4;
+ }
+ hptr++;
+ }
+ return bitstring;
+}
+
+/* octal to binary */
+
+char *oct2binary(char *uval, int bits)
+{
+ int first, dval, octc;
+ char *bitstring = (char *)malloc(1 + bits);
+ char *optr, *bptr, *ostring;
+ char binoct[4];
+
+ first = bits % 3;
+ octc = ((first == 0) ? 0 : 1) + (bits / 3); /* Number of octal characters */
+ ostring = (char *)malloc(1 + octc);
+
+ ostring = (char *)malloc(octc + 1);
+ strncpy(ostring, uval, octc);
+ *(ostring + octc) = '\0';
+ for (optr = ostring; *optr != '\0'; optr++) {
+
+ /* Catch 'x', 'X', etc., and convert to zero. */
+ /* Keep 'z'/'Z' as this must be handled differently */
+
+ if ((*optr == 'z') || (*optr == 'Z')) *optr = 'Z';
+ else if ((*optr == 'x') || (*optr == 'X')) *optr = '0';
+ }
+
+ optr = ostring;
+ bptr = bitstring;
+ while (*optr != '\0') {
+ switch(*optr) {
+ case 'Z':
+ strcpy(binoct, "ZZZ");
+ break;
+ case '0':
+ strcpy(binoct, "000");
+ break;
+ case '1':
+ strcpy(binoct, "001");
+ break;
+ case '2':
+ strcpy(binoct, "010");
+ break;
+ case '3':
+ strcpy(binoct, "011");
+ break;
+ case '4':
+ strcpy(binoct, "100");
+ break;
+ case '5':
+ strcpy(binoct, "101");
+ break;
+ case '6':
+ strcpy(binoct, "110");
+ break;
+ case '7':
+ strcpy(binoct, "11");
+ break;
+ }
+ if (first > 0) {
+ strncpy(bptr, binoct + (3 - first), first);
+ *(bptr + first) = '\0';
+ bptr += first;
+ first = 0;
+ }
+ else {
+ strcpy(bptr, binoct);
+ bptr += 3;
+ }
+ optr++;
+ }
+ return bitstring;
+}
+
+/* decimal to binary */
+
+char *dec2binary(char *uval, int bits)
+{
+ int first, dval, hexc;
+ char *bitstring = (char *)malloc(1 + bits);
+ char *hptr, *bptr, *hstring, *nval;
+ char binhex[5];
+
+ first = bits % 4;
+ hexc = ((first == 0) ? 0 : 1) + bits >> 2; /* Number of hex characters */
+ hstring = (char *)malloc(1 + hexc);
+
+ /* Scan integer value then convert to hex */
+ sscanf(uval, "%d", &dval);
+ sprintf(hstring, "%0*x", hexc, dval);
+
+ hptr = hstring;
+ bptr = bitstring;
+ while (*hptr != '\0') {
+ switch(*hptr) {
+ case '0':
+ strcpy(binhex, "0000");
+ break;
+ case '1':
+ strcpy(binhex, "0001");
+ break;
+ case '2':
+ strcpy(binhex, "0010");
+ break;
+ case '3':
+ strcpy(binhex, "0011");
+ break;
+ case '4':
+ strcpy(binhex, "0100");
+ break;
+ case '5':
+ strcpy(binhex, "0101");
+ break;
+ case '6':
+ strcpy(binhex, "0110");
+ break;
+ case '7':
+ strcpy(binhex, "0111");
+ break;
+ case '8':
+ strcpy(binhex, "1000");
+ break;
+ case '9':
+ strcpy(binhex, "1001");
+ break;
+ case 'a':
+ strcpy(binhex, "1010");
+ break;
+ case 'b':
+ strcpy(binhex, "1011");
+ break;
+ case 'c':
+ strcpy(binhex, "1100");
+ break;
+ case 'd':
+ strcpy(binhex, "1101");
+ break;
+ case 'e':
+ strcpy(binhex, "1110");
+ break;
+ case 'f':
+ strcpy(binhex, "1111");
+ break;
+ }
+ if (first > 0) {
+ strncpy(bptr, binhex + (4 - first), first);
+ bptr += first;
+ first = 0;
+ }
+ else {
+ strcpy(bptr, binhex);
+ bptr += 4;
+ }
+ hptr++;
+ }
+ return bitstring;
+}
+
+/*----------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec properties */
+/* hash table */
+/*----------------------------------------------------------------------*/
+
+struct nlist *output_props(struct hashlist *p, void *cptr)
+{
+ char *propval = (char *)(p->ptr);
+ FILE *outf = (FILE *)cptr;
+
+ fprintf(outf, ".%s(%s),\n", p->name, propval);
+ return NULL;
+}
+
+/*--------------------------------------------------------------*/
+/* Find the idx'th component of a net name. This pulls the */
+/* single wire name out of an indexed or concatenated array. */
+/* */
+/* Note that this routine does not handle nested braces. */
+/*--------------------------------------------------------------*/
+
+char *GetIndexedNet(char *netname, int ridx, struct cellrec *topcell)
+{
+ int i, alen, idx;
+ struct netrec wb;
+ char *sptr, *pptr, savc, *bptr;
+ static char *subname = NULL;
+ if (subname == NULL) subname = (char *)malloc(1);
+
+ if (*netname == '{') {
+ sptr = netname + 1;
+ i = 0;
+ while (i <= ridx) {
+ /* Advance to next comma or close-brace */
+ pptr = strchr(sptr, ',');
+ if (pptr == NULL) pptr = strchr(sptr, '}');
+ if (pptr == NULL) pptr = sptr + strlen(sptr) - 1;
+
+ savc = *pptr;
+ *pptr = '\0';
+
+ /* Does the array component at sptr have array bounds? */
+ GetBus(sptr, &wb, &topcell->nets);
+ if (wb.start != -1) {
+ alen = (wb.start - wb.end);
+ if (alen < 0) alen = -alen;
+ if (i + alen < ridx)
+ i += alen;
+ else {
+ if (wb.start < wb.end) idx = wb.start + (ridx - i);
+ else idx = wb.start - (ridx - i);
+ bptr = strrchr(sptr, '[');
+ if (bptr != NULL) *bptr = '\0';
+ subname = (char *)realloc(subname, strlen(sptr) + 10);
+ sprintf(subname, "%s[%d]", sptr, idx);
+ i = ridx;
+ if (bptr != NULL) *bptr = '[';
+ }
+ }
+ else {
+ if (i == ridx) {
+ subname = (char *)realloc(subname, strlen(sptr) + 1);
+ sprintf(subname, "%s", sptr);
+ }
+ i++;
+ }
+ *pptr = savc;
+ }
+ }
+ else {
+ GetBus(netname, &wb, &topcell->nets);
+ idx = (wb.start > wb.end) ? (wb.start - ridx) : (wb.start + ridx);
+ bptr = strrchr(netname, '[');
+ if (bptr != NULL) *bptr = '\0';
+
+ subname = (char *)realloc(subname, strlen(netname) + 10);
+ sprintf(subname, "%s[%d]", netname, idx);
+ if (bptr != NULL) *bptr = '[';
+ }
+ return subname;
+}
+
+/*--------------------------------------------------------------*/
+/* write_output */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int write_output(struct cellrec *topcell, unsigned char Flags, char *outname)
+{
+ FILE *outfptr = stdout;
+ int result = 0;
+ int nunconn = 0;
+ int arrayidx = -1;
+
+ GATE gate = (GATE)NULL;
+
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+
+ if (outname != NULL) {
+ outfptr = fopen(outname, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Cannot open file %s for writing.\n", outname);
+ return 1;
+ }
+ }
+
+ /* Write output module header */
+ fprintf(outfptr, "/* Verilog module written by vlog2Verilog (qflow) */\n");
+
+ if (Flags & IMPLICIT_POWER)
+ fprintf(outfptr, "/* With explicit power connections */\n");
+ if (!(Flags & MAINTAIN_CASE))
+ fprintf(outfptr, "/* With case-insensitive names (SPICE-compatible) */\n");
+ if (Flags & BIT_BLAST)
+ fprintf(outfptr, "/* With bit-blasted vectors */\n");
+ if (Flags & NONAME_POWER)
+ fprintf(outfptr, "/* With power connections converted to binary 1, 0 */\n");
+ fprintf(outfptr, "\n");
+
+ fprintf(outfptr, "module %s(\n", topcell->name);
+
+ if (Flags & IMPLICIT_POWER) {
+ fprintf(outfptr, " inout %s,\n", VddNet);
+ fprintf(outfptr, " inout %s,\n", GndNet);
+ }
+
+ for (port = topcell->portlist; port; port = port->next) {
+ if (port->name == NULL) continue;
+ switch(port->direction) {
+ case PORT_INPUT:
+ fprintf(outfptr, " input ");
+ break;
+ case PORT_OUTPUT:
+ fprintf(outfptr, " output ");
+ break;
+ case PORT_INOUT:
+ fprintf(outfptr, " inout ");
+ break;
+ }
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ fprintf(outfptr, "[%d:%d] ", net->start, net->end);
+ }
+ fprintf(outfptr, "%s", port->name);
+ if (port->next) fprintf(outfptr, ",");
+ fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, ");\n\n");
+
+ /* Declare all wires */
+
+ if (!(Flags & IMPLICIT_POWER) && !(Flags & NONAME_POWER)) {
+ fprintf(outfptr, "wire %s = 1'b1;\n", VddNet);
+ fprintf(outfptr, "wire %s = 1'b0;\n\n", GndNet);
+ }
+
+ RecurseHashTablePointer(&topcell->nets, output_wires, outfptr);
+ fprintf(outfptr, "\n");
+
+ /* Write instances in the order of the input file */
+
+ for (inst = topcell->instlist; inst; ) {
+ int nprops = RecurseHashTable(&inst->propdict, CountHashTableEntries);
+ fprintf(outfptr, "%s ", inst->cellname);
+ if (nprops > 0) {
+ fprintf(outfptr, "#(\n");
+ RecurseHashTablePointer(&inst->propdict, output_props, outfptr);
+ fprintf(outfptr, ") ");
+ }
+ if (inst->cellname)
+ fprintf(outfptr, "%s", inst->instname);
+ else {
+ fprintf(outfptr, "vlog2Verilog: No cell for instance %s\n", inst->instname);
+ result = 1; // Set error result but continue output.
+ }
+ if (inst->arraystart != -1) {
+ if (Flags & BIT_BLAST) {
+ if (arrayidx == -1) arrayidx = inst->arraystart;
+ fprintf(outfptr, "[%d]", arrayidx);
+ }
+ else {
+ fprintf(outfptr, " [%d:%d]", inst->arraystart, inst->arrayend);
+ }
+ }
+ fprintf(outfptr, " (\n");
+
+ // If there is a gate record read from LEF, keep a pointer to it.
+ if (GateInfo != NULL)
+ gate = (GATE)HashLookup(inst->cellname, &Lefhash);
+
+ if (Flags & IMPLICIT_POWER) {
+
+ /* If any LEF files were read, then get the power and */
+ /* ground net names from the LEF file definition. */
+
+ if (gate) {
+ int n;
+ u_char found = 0;
+ for (n = 0; n < gate->nodes; n++) {
+ if (gate->use[n] == PORT_USE_POWER) {
+ fprintf(outfptr, " .%s(%s),\n", gate->node[n], VddNet);
+ found++;
+ }
+ else if (gate->use[n] == PORT_USE_GROUND) {
+ fprintf(outfptr, " .%s(%s),\n", gate->node[n], GndNet);
+ found++;
+ }
+ if (found == 2) break;
+ }
+ }
+ else {
+ /* Fall back on VddNet and GndNet names */
+ fprintf(outfptr, " .%s(%s),\n", GndNet, GndNet);
+ fprintf(outfptr, " .%s(%s),\n", VddNet, VddNet);
+ }
+ }
+
+ /* Write each port and net connection */
+ for (port = inst->portlist; port; port = port->next) {
+
+ /* If writing explicit power net names, then watch */
+ /* for power connections encoded as binary, and */
+ /* convert them to the power bus names. */
+
+ if ((Flags & IMPLICIT_POWER) || (!(Flags & NONAME_POWER))) {
+ int brepeat = 0;
+ char is_array = FALSE, saveptr;
+ char *sptr = port->net, *nptr;
+ char *expand = (char *)malloc(1);
+
+ *expand = '\0';
+ if (*sptr == '{') {
+ is_array = TRUE;
+ sptr++;
+ expand = (char *)realloc(expand, 2);
+ strcpy(expand, "{");
+ }
+ while ((*sptr != '}') && (*sptr != '\0')) {
+ int nest = 0;
+
+ nptr = sptr + 1;
+ while ((*nptr != '\0') && (*nptr != ',')) {
+ if (*nptr == '{') nest++;
+ if (*nptr == '}') {
+ if (nest == 0) break;
+ else nest--;
+ }
+ nptr++;
+ }
+ saveptr = *nptr;
+ *nptr = '\0';
+
+ if (isdigit(*sptr) || (*sptr == '\'')) {
+ char *bptr = sptr;
+ if (sscanf(bptr, "%d", &brepeat) == 0) brepeat = -1;
+ while (isdigit(*bptr)) bptr++;
+
+ /* Is digit followed by "'" (fixed values 1 or 0)? */
+
+ if (*bptr == '\'') {
+ char *bitstring;
+ bptr++;
+
+ /* Important note: Need to check if 'x' is */
+ /* on an output, in which case it should be */
+ /* treated like 'z' (unconnected). */
+
+ /* Ports in verilog instances have no */
+ /* direction information so it is necessary */
+ /* to pull the information from the LEF */
+ /* record of the cell. */
+
+ if (gate) {
+ int n;
+ for (n = 0; n < gate->nodes; n++) {
+ if (!strcmp(gate->node[n], port->name)) {
+ switch (gate->direction[n]) {
+ case PORT_CLASS_INPUT:
+ port->direction = PORT_INPUT;
+ break;
+ case PORT_CLASS_OUTPUT:
+ port->direction = PORT_OUTPUT;
+ break;
+ default:
+ port->direction = PORT_INOUT;
+ break;
+ }
+ break;
+ }
+ }
+ }
+
+ if (port->direction != PORT_INPUT) {
+ char *xptr;
+ for (xptr = bptr; *xptr != '\0'; xptr++) {
+ if ((*xptr == 'X') || (*xptr == 'x'))
+ *xptr = 'z';
+ }
+ }
+
+ switch(*bptr) {
+ case 'd':
+ bitstring = dec2binary(bptr + 1, brepeat);
+ break;
+ case 'h':
+ bitstring = hex2binary(bptr + 1, brepeat);
+ break;
+ case 'o':
+ bitstring = oct2binary(bptr + 1, brepeat);
+ break;
+ default:
+ bitstring = strdup(bptr + 1);
+ break;
+ }
+
+ if (brepeat < 0) brepeat = strlen(bitstring);
+
+ if ((brepeat > 1) && (is_array == FALSE)) {
+ is_array = TRUE;
+ expand = (char *)realloc(expand, strlen(expand) + 2);
+ strcat(expand, "{");
+ }
+
+ bptr = bitstring;
+ while (*bptr != '\0') {
+ if (*bptr == '1') {
+ expand = (char *)realloc(expand,
+ strlen(expand) +
+ strlen(VddNet) + 2);
+ strcat(expand, VddNet);
+ }
+ else if (tolower(*bptr) == 'z') {
+ char unconnect[20];
+ /* Unconnected node: Make a new node name. */
+ /* This is a single bit, so it can be */
+ /* implicitly declared. */
+ sprintf(unconnect, "\\$_unconn_%d_ ", nunconn++);
+ expand = (char *)realloc(expand,
+ strlen(expand) + strlen(unconnect)
+ + 1);
+ strcat(expand, unconnect);
+ }
+ else { /* Note: If 'X', then ground it */
+ expand = (char *)realloc(expand,
+ strlen(expand) +
+ strlen(GndNet) + 2);
+ strcat(expand, GndNet);
+ }
+ brepeat--;
+ if (brepeat > 0)
+ strcat(expand, ",");
+ bptr++;
+ if (brepeat <= 0) break;
+ }
+ if (bptr == bitstring) {
+ fprintf(stderr, "Warning: Cannot parse \"%s\"\n", sptr);
+ }
+ while (brepeat > 0) {
+ if ((bptr > bitstring) && (*(bptr - 1) == '1')) {
+ expand = (char *)realloc(expand,
+ strlen(expand) +
+ strlen(VddNet) + 2);
+ strcat(expand, VddNet);
+ }
+ else { /* Note: If 'X', then ground it */
+ expand = (char *)realloc(expand,
+ strlen(expand) +
+ strlen(GndNet) + 2);
+ strcat(expand, GndNet);
+ }
+ brepeat--;
+ if (brepeat > 0)
+ strcat(expand, ",");
+ }
+ free(bitstring);
+ }
+
+ /* Otherwise add to "expand" verbatim */
+ else {
+ expand = (char *)realloc(expand, strlen(expand) +
+ strlen(sptr) + 1);
+ strcat(expand, sptr);
+ }
+ }
+ else {
+ /* Normal net name, add to "expand" */
+ expand = (char *)realloc(expand, strlen(expand) +
+ strlen(sptr) + 1);
+ strcat(expand, sptr);
+ }
+ if (saveptr == ',') {
+ expand = (char *)realloc(expand, strlen(expand) + 2);
+ strcat(expand, ",");
+ }
+ *nptr = saveptr;
+ sptr = nptr;
+ if (saveptr != '\0') sptr++;
+ }
+
+ if (is_array) {
+ expand = (char *)realloc(expand, strlen(expand) + 2);
+ strcat(expand, "}");
+ }
+
+ /* Replace port->net */
+
+ free(port->net);
+ port->net = expand;
+ }
+ fprintf(outfptr, " .%s(", port->name);
+ if ((Flags & BIT_BLAST) && (arrayidx != -1)) {
+ /* Find the index from the start and pull that item from port->net */
+ int ridx;
+ ridx = arrayidx - inst->arraystart;
+ if (ridx < 0) ridx = -ridx;
+ fprintf(outfptr, "%s", GetIndexedNet(port->net, ridx, topcell));
+ }
+ else
+ fprintf(outfptr, "%s", port->net);
+ fprintf(outfptr, ")");
+ if (port->next) fprintf(outfptr, ",");
+ fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, ");\n\n");
+
+ /* For bit-blasted output, output each element of an array separately. */
+ if (Flags & BIT_BLAST) {
+ if (arrayidx == inst->arrayend) {
+ inst = inst->next;
+ arrayidx = -1;
+ }
+ else if (arrayidx < inst->arrayend)
+ arrayidx++;
+ else if (arrayidx > inst->arrayend)
+ arrayidx--;
+ }
+ else
+ inst = inst->next;
+ }
+
+ if (Flags & ADD_ANTENNA) {
+ char *antennapin = NULL;
+ GATE gate, acell = NULL;
+ double asize;
+
+ /* Find the cell name that matches the antenna cell */
+ /* Can't use the hash table here because name may be a */
+ /* prefix. If more than one cell matches by prefix, */
+ /* then use the cell with the smallest area. */
+
+ for (gate = GateInfo; gate; gate = gate->next) {
+ if (!strncmp(gate->gatename, AntennaCell, strlen(AntennaCell))) {
+ if (!strcmp(gate->gatename, AntennaCell)) {
+ acell = gate;
+ break;
+ }
+ else if (acell == NULL) {
+ acell = gate;
+ }
+ else {
+ if (gate->width < acell->width) {
+ acell = gate;
+ }
+ }
+ }
+ }
+
+ if (acell) {
+ int i;
+ /* Find the node that isn't a power pin */
+ for (i = 0; i < acell->nodes; i++) {
+ if (acell->use[i] != PORT_USE_POWER &&
+ acell->use[i] != PORT_USE_GROUND) {
+ antennapin = acell->node[i];
+ break;
+ }
+ }
+ }
+
+ if (antennapin) {
+ int antcnt = 0;
+
+ /* Add antenna cells to all module inputs */
+
+ for (port = topcell->portlist; port; port = port->next) {
+ if (port->name == NULL) continue;
+ switch(port->direction) {
+ case PORT_INPUT:
+ fprintf(outfptr, "%s antenna_%d ", acell->gatename, antcnt);
+
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ fprintf(outfptr, "[%d:%d] ", net->start, net->end);
+ }
+
+ fprintf(outfptr, "(\n");
+ fprintf(outfptr, " .%s(%s)\n", antennapin, port->name);
+ fprintf(outfptr, ");\n\n");
+ antcnt++;
+ break;
+ }
+ }
+ }
+ }
+
+ /* End the module */
+ fprintf(outfptr, "endmodule\n");
+
+ if (outname != NULL) fclose(outfptr);
+
+ fflush(stdout);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "vlog2Verilog [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "vlog2Verilog converts a netlist in one verilog style to\n");
+ fprintf(outf, "another. LEF files may be given as inputs to determine\n");
+ fprintf(outf, "power and ground net names for cells.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -h Print this message\n");
+ fprintf(outf, " -o <path> Set output filename (otherwise output is on stdout).\n");
+ fprintf(outf, " -p Don't add power nodes to instances\n");
+ fprintf(outf, " (only nodes present in the instance used)\n");
+ fprintf(outf, " -b Remove vectors (bit-blasted)\n");
+ fprintf(outf, " -c Case-insensitive output (SPICE compatible) \n");
+ fprintf(outf, " -n Convert power nets to binary 1 and 0\n");
+ fprintf(outf, " -a <name> Add antenna cells to module input pins.\n");
+ fprintf(outf, " -l <path> Read LEF file from <path>\n");
+ fprintf(outf, " -v <name> Use <name> for power net (default \"Vdd\")\n");
+ fprintf(outf, " -g <name> Use <name> for ground net (default \"Gnd\")\n");
+
+} /* helpmessage() */
+
diff --git a/src/vlogFanout.c b/src/vlogFanout.c
new file mode 100644
index 0000000..4fc4aef
--- /dev/null
+++ b/src/vlogFanout.c
@@ -0,0 +1,1735 @@
+/*
+ *---------------------------------------------------------------------------
+ * vlogFanout vlog_input [vlog_output]
+ *
+ * vlogFanout[.c] parses a structural verilog netlist. The fanout is analyzed,
+ * and fanout of each gate is counted. A value to parameterize the driving
+ * cell will be output.
+ * Fanouts exceeding a maximum are broken into (possibly hierarchical)
+ * buffer trees. Eventually, a critical path can be identified, and the
+ * gates sized to improve it.
+ *
+ * Original: fanout.c by Steve Beccue
+ * New: vlogFanout.c by Tim Edwards.
+ * changes: 1) Input and output format changed from RTL verilog to BDNET
+ * for compatibility with the existing digital design flow.
+ * 2) Gate format changed to facilitate entering IBM data
+ * 3) Code changed/optimized in too many ways to list here.
+ *
+ * Update 4/8/2013:
+ * Removing dependence upon the naming convention of the original
+ * technology used with this tool. Instead, the format of the naming
+ * convention is passed to the tool using "-s <separator>", and parsing
+ * the remaining name for unique identifiers.
+ *
+ * Update 10/8/2013:
+ * Changed input file format from BDNET to BLIF
+ *
+ * Update 10/21/2013:
+ * Removed most of the dependencies on fixed-length arrays
+ *
+ * Update 5/13/2015:
+ * Added hash functions, which have been on the "to do" list for a
+ * while. Greatly speeds up the processing, especially for large
+ * netlists.
+ *
+ * Replaced the gate.cfg file parser with the liberty file parser
+ * from the "liberty2tech" code, which is no longer needed. The
+ * gate.cfg format was essentially useless as it failed to track
+ * which pins in a cell are inputs and which are outputs.
+ *
+ * Moving the clock tree generator into this module, because I have
+ * figured out a way to do this in one stage instead of two, using
+ * the swap_group capability of the graywolf placement tool to find
+ * the optimal groupings.
+ *
+ * Update 5/7/2018:
+ * Separated clock buffers from other buffers. Identify clock inputs
+ * on flops and latches and trace back to a common clock pin or pins.
+ *
+ * Update 6/15/2018:
+ * Finally got around to doing dynamic string allocation on the input,
+ * which avoids issues of having I/O lines that are arbitrarily long
+ * crashing the program.
+ *
+ * Update 11/26/2018:
+ * Reworked the entire tool to operate on an input verilog netlist
+ * instead of a BLIF file, given the limitations of the BLIF format
+ * and the need to completely rewrite the BLIF handling scripts in
+ * order to maintain use of a badly outdated format. Also, modified
+ * the code so that iterative calls are run inside vlogFanout instead
+ * of requiring vlogFanout to be run multiple times, so that the
+ * liberty file and netlist do not have to be re-read on each pass.
+ *
+ * Update 1/17/2019:
+ * Added handling of pins which are buses.
+ *---------------------------------------------------------------------------
+ *
+ * Revision 1.3 2008/09/09 21:24:30 steve_beccue
+ * changed gate strengths in code. Inserted inverters. 2nd pass on
+ * insert inverters does not work.
+ *
+ * Revision 1.2 2008/09/04 14:25:59 steve_beccue
+ * added helpmessage and id
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h> /* for getopt() */
+#include <string.h>
+#include <ctype.h> /* for isdigit() */
+#include <math.h>
+
+#include "hash.h" /* for hash table functions */
+#include "readliberty.h" /* liberty file database */
+#include "readverilog.h" /* verilog parser */
+
+#define FALSE 0
+#define TRUE 1
+#define MAXLINE 512
+
+char *Inputfname;
+char *Outputfname;
+char *Buffername = NULL;
+char *Clkbufname = NULL;
+char *buf_in_pin = NULL;
+char *clkbuf_in_pin = NULL;
+char *buf_out_pin = NULL;
+char *clkbuf_out_pin = NULL;
+char *Ignorepath = NULL;
+char SuffixIsNumeric;
+int GatePrintFlag = 0;
+int NodePrintFlag = 0;
+int VerboseFlag = 0;
+int skip_eol = 0;
+
+int Topfanout = 0;
+int Inputfanout = 0;
+double Topload = 0.0;
+double Inputload = 0.0;
+double Topratio = 0.0;
+int Changed_count = 0; // number of gates changed
+int Buffer_count = 0; // number of buffers added
+int stren_err_counter = 0;
+double MaxOverload = 0.0;
+
+int MaxFanout = 16; // Maximum fanout per node allowed without
+ // additional buffering.
+double MaxLatency = 1000.0; // Maximum variable latency (ps) for which we
+ // are solving. Represents the largest delay
+ // allowed for any gate caused by total load
+ // capacitance. This is empirically derived.
+double MaxOutputCap = 30.0; // Maximum capacitance for an output node (fF).
+ // Outputs should be able to drive this much
+ // capacitance within MaxLatency time (ps).
+double WireCap = 10.0; // Base capacitance for an output node, estimate
+ // of average wire capacitance (fF).
+
+struct Gatelist {
+ char *gatename;
+ Cell *gatecell;
+ char *suffix; // points to position in gatename, not allocated
+ char *separator;
+ int num_inputs;
+ double Cint;
+ double delay;
+ double strength;
+} Gatelist_;
+
+struct hashtable Gatehash;
+
+struct Nodelist {
+ char ignore;
+ char *nodename;
+ struct Gatelist *outputgate;
+ double outputgatestrength;
+ int type;
+ int clock;
+ int num_inputs;
+ double total_load;
+ double ratio; // drive strength to total_load ratio
+ // For net buffer trees
+ int num_buf; // Number of buffers to split the net
+ int curcount; // Active count for fanout buffering trees
+} Nodelist_;
+
+struct hashtable Nodehash;
+struct hashtable Bushash;
+
+struct Bus {
+ int imax;
+ int imin;
+} Bus_;
+
+struct Drivelist {
+ char *Separator; // Separator (e.g., "X")
+ char *DriveType; // Suffix name (e.g., "1")
+ int NgatesIn; // Number of gates with this suffix in input
+ int NgatesOut; // Number of gates with this suffix in output
+} Drivelist_;
+
+struct hashtable Drivehash;
+
+struct Baselist {
+ char *BaseName; // gate base name (e.g., "INV")
+ int Ndrives; // number of different drive types for this gate
+ struct Gatelist **gates; // list of pointers to gates with
+} Baselist_;
+
+struct hashtable Basehash;
+
+enum states_ {NONE, INPUTS, OUTPUTS, GATENAME, PINNAME, INPUTNODE, CLOCKNODE,
+ OUTPUTNODE, ENDMODEL, ERROR};
+enum nodetype_ {UNKNOWN, INPUT, CLOCK, OUTPUT, INPUTPIN, OUTPUTPIN, INOUTPIN};
+
+int read_gate_file(char *gate_file_name, char *separator);
+void read_ignore_file(char *ignore_file_name);
+struct Gatelist *GatelistAlloc();
+struct Nodelist *NodelistAlloc();
+struct Drivelist *DrivelistAlloc();
+struct Baselist *BaselistAlloc();
+void showgatelist(void);
+void helpmessage(void);
+struct Nodelist *registernode(char *nodename, int type, struct Gatelist *gl,
+ char *pinname);
+void shownodes(void);
+void resize_gates(struct cellrec *topcell, int doLoadBalance, int doFanout);
+void write_output(struct cellrec *topcell, FILE *outfptr, int doLoadBalance,
+ int doFanout);
+struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload);
+void count_gatetype(struct Gatelist *gl, int num_in, int num_out);
+
+/*
+ *---------------------------------------------------------------------------
+ * find_suffix ---
+ *
+ * Given a gate name, return the part of the name corresponding to the
+ * suffix. That is, find the last occurrance of the string separator
+ * and return a pointer to the following character. Note that a NULL
+ * separator means there is no suffix, vs. an emptry string separator,
+ * which means that the suffix encompasses all digits at the end of
+ * the gate name.
+ *---------------------------------------------------------------------------
+ */
+
+char *find_suffix(char *gatename, char *separator)
+{
+ char *tsuf, *gptr;
+ char *suffix = NULL;
+
+ if (separator == NULL) {
+ return NULL;
+ }
+ else if (*separator == '\0') {
+ suffix = gatename + strlen(gatename) - 1;
+ while (isdigit(*suffix)) suffix--;
+ suffix++;
+ }
+ else {
+ gptr = gatename;
+ while ((tsuf = strstr(gptr, separator)) != NULL) {
+ suffix = tsuf;
+ gptr = tsuf + 1;
+ }
+ if (suffix != NULL)
+ suffix += strlen(separator);
+ }
+ return suffix;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Check if a liberty file function describes a buffer. Since this is the
+ * function of the output pin, it needs only be the input pin (e.g.,
+ * func(Q) = "A"). However, some liberty files repeat the output pin (e.g.,
+ * func(Q) = "Q = A"). Check for both styles.
+ *---------------------------------------------------------------------------
+ */
+
+int is_buffer_func(char *func_text, char *pin_in, char *pin_out) {
+ char *eqptr, esav, *tptr;
+
+ if (!strcmp(func_text, pin_in)) return 1;
+
+ else if ((eqptr = strchr(func_text, '=')) != NULL) {
+ tptr = eqptr + 1;
+ while (isspace(*tptr) && (*tptr != '\0')) tptr++;
+ while ((eqptr > func_text) && isspace(*(eqptr - 1))) eqptr--;
+ esav = *eqptr;
+ *eqptr = '\0';
+ if (!strcmp(func_text, pin_out) && (*tptr != '\0') &&
+ !strcmp(tptr, pin_in)) {
+ *eqptr = esav;
+ return 1;
+ }
+ *eqptr = esav;
+ }
+ return 0;
+}
+
+typedef struct _gaterec {
+ char *path; /* Path to library */
+ char *sep; /* Gate name separator ("-" if none) */
+} GateRec;
+
+/*----------------------------------------------------------------------*/
+/* Insert buffers to reduce fanout */
+/* */
+/* This is done in two passes to avoid changing the hash entries while */
+/* iterating through them. Nodes needed buffering are marked with the */
+/* number of buffers required. Then, the nodes are parsed again and */
+/* buffers are added where marked. */
+/* */
+/* Return the highest index (plus one) of any component added, so that */
+/* insert_buffers can be called subsequent times without generating the */
+/* same name for any component. This value is passed back to the */
+/* subroutine as "cidx". */
+/*----------------------------------------------------------------------*/
+
+int insert_buffers(struct cellrec *topcell, int cidx)
+{
+ int i;
+ int hier;
+ int slen;
+ struct Nodelist *nl = NULL;
+ struct Nodelist *nltest;
+ char nodename[MAXLINE];
+ char instname[MAXLINE];
+ char *spos;
+
+ struct Gatelist *glbuf, *clkbuf;
+ struct Gatelist *gl;
+
+ struct portrec *port, *newport;
+ struct instance *inst, *newinst;
+
+ // Find the gate record corresponding to the buffer name
+ glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
+
+ // Find the gate record corresponding to the clock buffer name
+ clkbuf = (struct Gatelist *)HashLookup(Clkbufname, &Gatehash);
+
+ Buffer_count = 0;
+ if ((Topfanout > MaxFanout) || (Inputfanout > MaxFanout)) {
+
+ /* Mark nets for inserting buffer trees */
+
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
+ while (nl != NULL) {
+ if (nl->ignore == FALSE) {
+
+ // Nets with no driver must be module inputs. Mainly,
+ // this condition rejects power and ground nodes.
+
+ if ((nl->num_inputs > MaxFanout) && ((nl->outputgatestrength != 0.0)
+ || (nl->type == INPUTPIN))) {
+ double d;
+ int stages, n, mfan;
+
+ // Find number of hierarchical stages (plus one) needed
+
+ mfan = nl->num_inputs;
+ stages = 1;
+ n = MaxFanout;
+ while (mfan > MaxFanout) {
+ mfan = nl->num_inputs / n;
+ n *= MaxFanout;
+ stages++;
+ }
+
+ // Find the floor of the number of fanouts per buffer
+
+ d = pow((double)nl->num_inputs, (double)(1.0 / stages));
+ n = (int)((double)nl->num_inputs / d);
+
+ // Split network into reasonably balanced groups of
+ // the same (or almost) fanout.
+
+ nl->num_buf = n;
+ nl->curcount = n - 1;
+ Buffer_count += n;
+ }
+ }
+ nl = (struct Nodelist *)HashNext(&Nodehash);
+ }
+ }
+
+ /* Parse all instances and adjust net names to account for buffer trees. */
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ /* Check each port and net connection */
+ gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash);
+ if (gl == NULL) continue;
+ for (port = inst->portlist; port; port = port->next) {
+ if (port->direction != PORT_OUTPUT) {
+
+ if (VerboseFlag) printf("\nInput node %s", port->net);
+ nl = (struct Nodelist *)HashLookup(port->net, &Nodehash);
+ if (nl == NULL) {
+ fprintf(stderr, "vlogFanout: Port net %s not in hash\n", port->net);
+ continue;
+ }
+ if (nl->num_buf > 0) {
+ hier = 0;
+ nltest = nl;
+ sprintf(nodename, "%s", nl->nodename);
+ while (1) {
+ int is_escaped;
+
+ slen = strlen(nodename);
+ spos = nodename + slen - 1;
+ is_escaped = (*nodename == '\\') ? TRUE : FALSE;
+ if ((is_escaped == TRUE) && (*spos == ' ')) spos--;
+ if (*spos == ']') {
+ /* Avoid downstream problems: */
+ /* recast "[X]_bF$bufN" as _X_bF$bufN" */
+ char *dptr = nodename + slen - 1;
+ while (dptr >= nodename && *dptr != '[') dptr--;
+ if (dptr >= nodename) *dptr = '_';
+ if (dptr > nodename && *(dptr - 1) == ' ') {
+ /* There was a space in front of the vector */
+ /* delimiter, so move everything back one. */
+ memmove(dptr - 1, dptr, strlen(dptr));
+ spos--;
+ is_escaped = TRUE;
+ }
+ }
+ else {
+ spos++;
+ }
+ sprintf(spos, "_bF$buf%d%s", nl->curcount,
+ ((is_escaped == TRUE) ? " " : ""));
+
+ /* For buffer trees of depth > 1, there will be */
+ /* an existing node name wih the _bufN extension */
+ /* that is in the node hash table. If so, then */
+ /* add the prefix "_hierM". Test again in case */
+ /* the buffer tree is even deeper, incrementing */
+ /* until the name is unique. */
+
+ nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash);
+ if (nltest == NULL) break;
+ if (nltest->outputgate == NULL) break;
+ sprintf(spos, "_hier%d%s", hier,
+ ((is_escaped == TRUE) ? " " : ""));
+ hier++;
+ }
+
+ /* Increment input count and load cap on new node */
+ registernode(nodename, INPUT, gl, port->name);
+
+ nl->curcount--;
+ if (nl->curcount < 0) nl->curcount = nl->num_buf - 1;
+
+ /* Reassign the port's net name */
+ free(port->net);
+ port->net = strdup(nodename);
+ }
+ }
+ }
+ }
+
+ /* Insert any added buffers */
+
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
+ while (nl != NULL) {
+ for (i = nl->num_buf - 1; i >= 0; i--) {
+ hier = 0;
+ nltest = nl;
+ sprintf(nodename, "%s", nl->nodename);
+ while (1) {
+ int is_escaped;
+
+ slen = strlen(nodename);
+ spos = nodename + slen - 1;
+ is_escaped = (*nodename == '\\') ? TRUE : FALSE;
+ if ((is_escaped == TRUE) && (*spos == ' ')) spos--;
+ if (*spos == ']') {
+ /* Avoid downstream problems: */
+ /* recast "[X]_bF$bufN" as _X_bF$bufN" */
+ char *dptr = nodename + slen - 1;
+ while (dptr >= nodename && *dptr != '[') dptr--;
+ if (dptr >= nodename) *dptr = '_';
+ if (dptr > nodename && *(dptr - 1) == ' ') {
+ memmove(dptr - 1, dptr, strlen(dptr));
+ spos--;
+ }
+ }
+ else {
+ spos++;
+ }
+ sprintf(spos, "_bF$buf%d%s", i,
+ ((is_escaped == TRUE) ? " " : ""));
+
+ /* For buffer trees of depth > 1, there will be */
+ /* an existing node name wih the _bufN extension */
+ /* that is in the node hash table. If so, then */
+ /* add the prefix "_hierM". Test again in case */
+ /* the buffer tree is even deeper, incrementing */
+ /* M until the name is unique. */
+
+ nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash);
+ if (nltest == NULL) break;
+ if (nltest->outputgate == NULL) break;
+ sprintf(spos, "_hier%d%s", hier,
+ ((is_escaped == TRUE) ? " " : ""));
+ hier++;
+ }
+
+ if (nl->clock == TRUE) {
+ /* Prepend clock buffer to instance list */
+ newinst = PrependInstance(topcell, Clkbufname);
+ sprintf(instname, "%s_insert%d", Clkbufname, cidx);
+ newinst->instname = strdup(instname);
+ newport = InstPort(newinst, clkbuf_in_pin, nl->nodename);
+ newport = InstPort(newinst, clkbuf_out_pin, nodename);
+
+ /* Register the new node name */
+ Net(topcell, nodename);
+ registernode(nodename, OUTPUT, clkbuf, clkbuf_out_pin);
+ }
+ else {
+ /* Prepend regular buffer to instance list */
+ newinst = PrependInstance(topcell, Buffername);
+ sprintf(instname, "%s_insert%d", Buffername, cidx);
+ newinst->instname = strdup(instname);
+ newport = InstPort(newinst, buf_in_pin, nl->nodename);
+ newport = InstPort(newinst, buf_out_pin, nodename);
+
+ /* Register the new node name */
+ Net(topcell, nodename);
+ registernode(nodename, OUTPUT, glbuf, buf_out_pin);
+ }
+ cidx++;
+ }
+ nl->num_inputs = nl->num_buf;
+ nl->num_buf = 0;
+ nl = (struct Nodelist *)HashNext(&Nodehash);
+ }
+ return cidx;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Read a file of nets for which we should ignore fanout. Typically this
+ * would include the power and ground nets, but may include other static
+ * nets with non-critical timing.
+ *---------------------------------------------------------------------------
+ */
+
+void read_ignore_file(char *ignore_file_name)
+{
+ struct Nodelist *nl;
+ FILE *ignorefptr;
+ char line[MAXLINE]; /* One net per line, should not need dynamic allocation */
+ char *s, *sp;
+
+ if (!(ignorefptr = fopen(ignore_file_name, "r"))) {
+ fprintf(stderr, "vlogFanout: Couldn't open %s as ignore file.\n",
+ ignore_file_name);
+ fflush(stderr);
+ return;
+ // This is only a warning. It will not stop execution of vlogFanout
+ }
+
+ while ((s = fgets(line, MAXLINE, ignorefptr)) != NULL) {
+ // One net name per line
+ while (isspace(*s)) s++;
+ sp = s;
+ while (*sp != '\0' && *sp != '\n' && !isspace(*sp)) sp++;
+ *sp = '\0';
+
+ nl = (struct Nodelist *)HashLookup(s, &Nodehash);
+ if (nl != NULL) {
+ nl->ignore = (char)1;
+ }
+ }
+ fclose(ignorefptr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Call read_liberty() to read in cell information from a Liberty file,
+ * and then hash the resulting list.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int read_gate_file(char *gate_file_name, char *separator)
+{
+ int i, j, k, ind, format = -1;
+ int gatecount;
+ char *s, *t, *ss, ssave;
+ struct Gatelist *gl;
+ struct Baselist *bl;
+ Cell *cells, *curcell;
+ Pin *curpin;
+
+ gatecount = 0;
+ cells = read_liberty(gate_file_name, NULL);
+ for (curcell = cells; curcell != NULL; curcell = curcell->next) {
+ if (curcell->name == NULL) continue; /* undefined/unused cell */
+
+ gl = GatelistAlloc();
+ gl->gatename = strdup(curcell->name);
+ gl->suffix = find_suffix(gl->gatename, separator);
+ gl->separator = separator;
+ gl->gatecell = curcell;
+
+ get_values(curcell, &gl->delay, &gl->Cint);
+
+ gl->num_inputs = 0;
+ for (curpin = curcell->pins; curpin; curpin = curpin->next)
+ if (curpin->type == PIN_INPUT || curpin->type == PIN_CLOCK)
+ gl->num_inputs++;
+
+ /* The "MaxLatency" is empirically derived. Since gl->delay */
+ /* is in ps/fF, and strength is compared directly to total */
+ /* load capacitance, MaxLatency is effectively in units of ps */
+ /* and represents the maximum latency due to load capacitance */
+ /* for any gate in the circuit after making gate strength */
+ /* substitutions (this does not include internal, constant */
+ /* delays in each gate). */
+
+ // (Diagnostic, for debug)
+ // fprintf(stdout, "Parsing cell \"%s\", \"%s\", function \"%s\"\n",
+ // gl->gatename, gl->gatecell->name, gl->gatecell->function);
+
+ gl->strength = MaxLatency / gl->delay;
+ HashPtrInstall(gl->gatename, gl, &Gatehash);
+ gatecount++;
+
+ /* Install prefix in Basehash. Note that prefix contains the */
+ /* separator string, if any. */
+
+ if ((s = gl->suffix) == NULL)
+ ind = strlen(gl->gatename);
+ else
+ ind = (int)(s - gl->gatename);
+
+ ssave = gl->gatename[ind];
+ gl->gatename[ind] = '\0';
+ bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash);
+ if (bl == NULL) {
+ bl = BaselistAlloc();
+ HashPtrInstall(gl->gatename, bl, &Basehash);
+ bl->BaseName = strdup(gl->gatename);
+ }
+ gl->gatename[ind] = ssave;
+
+ // Note: this code assumes that there are no repeats in
+ // the liberty file gate list (there shouldn't be).
+
+ if (bl->Ndrives == 0)
+ bl->gates = (struct Gatelist **)malloc(sizeof(struct Gatelist *));
+ else
+ bl->gates = (struct Gatelist **)realloc(bl->gates,
+ (bl->Ndrives + 1) * sizeof(struct Gatelist *));
+ bl->gates[bl->Ndrives] = gl;
+ bl->Ndrives++;
+ }
+ return gatecount;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+struct Gatelist* GatelistAlloc()
+{
+ struct Gatelist *gl;
+
+ gl = (struct Gatelist*)malloc(sizeof(struct Gatelist));
+ gl->gatename = NULL;
+ gl->suffix = NULL;
+ gl->num_inputs = 0;
+ gl->Cint = 0.0;
+ gl->delay = 0.0;
+ gl->strength = 0.0;
+ return gl;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+struct Drivelist *DrivelistAlloc()
+{
+ struct Drivelist *dl;
+
+ dl = (struct Drivelist *)malloc(sizeof(struct Drivelist));
+ dl->NgatesIn = 0;
+ dl->NgatesOut = 0;
+ dl->DriveType = NULL;
+ dl->Separator = NULL;
+ return dl;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+struct Nodelist* NodelistAlloc()
+{
+ struct Nodelist *nl;
+
+ nl = (struct Nodelist *)malloc(sizeof(struct Nodelist));
+ nl->nodename = NULL;
+ nl->ignore = FALSE;
+ nl->outputgate = NULL;
+ nl->outputgatestrength = 0.0;
+ nl->type = UNKNOWN;
+ nl->total_load = 0.0;
+ nl->num_inputs = 0;
+ nl->num_buf = 0; // Tree expansion of node
+ nl->curcount = 0;
+ nl->clock = FALSE;
+ return nl;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+struct Baselist *BaselistAlloc()
+{
+ struct Baselist *bl;
+
+ bl = (struct Baselist *)malloc(sizeof(struct Baselist));
+ bl->BaseName = NULL;
+ bl->Ndrives = 0;
+ bl->gates = NULL;
+ return bl;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+void showgatelist(void)
+{
+ struct Gatelist *gl;
+ Cell *curcell;
+ Pin *curpin;
+ double pincap;
+
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
+ while (gl != NULL) {
+
+ printf("\n\ngate: %s with %d inputs and %g drive strength\n",
+ gl->gatename, gl->num_inputs, gl->strength);
+ printf("%g ", gl->Cint);
+
+ curcell = gl->gatecell;
+ for (curpin = curcell->pins; curpin; curpin = curpin->next) {
+ if (curpin->type == PIN_INPUT || curpin->type == PIN_CLOCK) {
+ get_pincap(curcell, curpin->name, &pincap);
+ printf("%g ", pincap);
+ }
+ }
+ gl = (struct Gatelist *)HashNext(&Gatehash);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+struct Nodelist *registernode(char *nodename, int type, struct Gatelist *gl,
+ char *pinname)
+{
+ struct Nodelist *nl;
+ double pincap;
+ char *dptr;
+
+ nl = (struct Nodelist *)HashLookup(nodename, &Nodehash);
+
+ if (nl == NULL) {
+ nl = NodelistAlloc();
+ nl->nodename = strdup(nodename);
+ if (type == OUTPUT) nl->outputgate = NULL;
+ HashPtrInstall(nodename, nl, &Nodehash);
+ nl->type = type;
+ nl->outputgate = NULL;
+
+ if ((dptr = strrchr(nodename, '[')) != NULL) {
+ struct Bus *newbus = (struct Bus *)malloc(sizeof(struct Bus));
+ int idx;
+ *dptr = '\0';
+ sscanf(dptr + 1, "%d", &idx);
+ newbus->imax = newbus->imin = idx;
+ HashPtrInstall(nodename, newbus, &Bushash);
+ *dptr = '[';
+ }
+ }
+ else {
+ if ((dptr = strrchr(nodename, '[')) != NULL) {
+ struct Bus *newbus;
+ int idx;
+ *dptr = '\0';
+ newbus = (struct Bus *)HashLookup(nodename, &Bushash);
+ sscanf(dptr + 1, "%d", &idx);
+ if (idx < newbus->imin) newbus->imin = idx;
+ if (idx > newbus->imax) newbus->imax = idx;
+ *dptr = '[';
+ }
+ }
+
+ if (type == OUTPUT) {
+ nl->outputgate = gl;
+ if (gl != NULL) {
+ nl->outputgatestrength = gl->strength;
+ nl->total_load += gl->Cint;
+ count_gatetype(gl, 1, 1);
+ }
+ }
+ else if (type == INPUT || type == CLOCK) {
+ if (gl != NULL) {
+ get_pincap(gl->gatecell, pinname, &pincap);
+ nl->total_load += pincap;
+ nl->num_inputs++;
+ }
+ }
+ if (type == CLOCK) nl->clock = TRUE;
+
+ if ((nl->type != INPUTPIN) && (nl->type != OUTPUTPIN) &&
+ (nl->type != INOUTPIN) && (gl == NULL)) {
+ fprintf(stderr, "\nError: no output gate for net %s\n", nodename);
+ fflush(stderr);
+ }
+ return nl;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+void count_gatetype(struct Gatelist *gl, int num_in, int num_out)
+{
+ struct Drivelist *dl;
+ char *s, *nptr, *tsuf;
+ int g;
+
+ if ((s = gl->suffix) == NULL)
+ return;
+
+ dl = (struct Drivelist *)HashLookup(s, &Drivehash);
+ if (dl == NULL) {
+
+ // New drive type found
+
+ dl = DrivelistAlloc();
+ HashPtrInstall(s, dl, &Drivehash);
+ dl->DriveType = strdup(s);
+ dl->Separator = gl->separator;
+ }
+
+ dl->NgatesIn += num_in; // Number of these gates before processing
+ dl->NgatesOut += num_out; // Number of these gates after processing
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+void shownodes(void)
+{
+ struct Nodelist *nl;
+ int i;
+
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
+ while (nl != NULL) {
+ printf("\n\nnode: %s with %d fanout and %g fF cap",
+ nl->nodename, nl->num_inputs, nl->total_load);
+ printf("\ndriven by %s, with %g strength.\n",
+ nl->outputgate->gatename, nl->outputgatestrength);
+ nl = (struct Nodelist *)HashNext(&Nodehash);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec nets hash table */
+/*--------------------------------------------------------------------------*/
+
+struct nlist *output_wires(struct hashlist *p, void *cptr)
+{
+ struct netrec *net;
+ FILE *outf = (FILE *)cptr;
+
+ net = (struct netrec *)(p->ptr);
+
+ /* Ignore any net which is a hardwired 1/0 bit list */
+
+ if (p->name[0] == '\'') return NULL;
+ if (isdigit(p->name[0])) {
+ char c, *dptr;
+
+ dptr = p->name;
+ while (isdigit(*dptr)) dptr++;
+ if (*dptr == '\0') return NULL;
+ else if (*dptr == '\'') {
+ c = *(dptr + 1);
+ if (c == 'b' || c == 'h' || c == 'd' || c == 'o')
+ return NULL;
+ }
+ }
+
+ fprintf(outf, "wire ");
+ if (net->start >= 0 && net->end >= 0) {
+ fprintf(outf, "[%d:%d] ", net->start, net->end);
+ }
+ fprintf(outf, "%s ;\n", p->name);
+ return NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* Recursion callback function for each item in the cellrec properties */
+/* hash table */
+/*----------------------------------------------------------------------*/
+
+struct nlist *output_props(struct hashlist *p, void *cptr)
+{
+ char *propval = (char *)(p->ptr);
+ FILE *outf = (FILE *)cptr;
+
+ fprintf(outf, ".%s(%s),\n", p->name, propval);
+ return NULL;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Resize gates
+ *---------------------------------------------------------------------------
+ */
+
+void resize_gates(struct cellrec *topcell, int doLoadBalance, int doFanout)
+{
+ char *s, *t;
+ char instname[MAXLINE];
+ char *stren, *orig;
+ int gateinputs;
+ int pincount;
+ int needscorrecting;
+ int i;
+ struct Gatelist *gl;
+ struct Gatelist *glbest, *bbest;
+ struct Gatelist *glbuf;
+ struct Nodelist *nl;
+ struct Drivelist *dl;
+ double inv_size;
+
+ struct netrec *net;
+ struct portrec *port, *newport;
+ struct instance *inst, *newinst;
+ struct cellrec *sizedcell;
+ int cidx;
+
+ Changed_count = 0;
+ needscorrecting = 0;
+
+ // Find the gate record corresponding to the buffer name
+ glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
+
+ /* If there are situations where a load is high but the fanout is low, */
+ /* and insertion of a buffer will reduce the overall delay, then insert a */
+ /* buffer. */
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ needscorrecting = FALSE;
+
+ /* Find the gate's output node and determine if the gate is resized */
+
+ gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash);
+ if (gl == NULL) continue;
+
+ for (port = inst->portlist; port; port = port->next)
+ if (port->direction == PORT_OUTPUT)
+ break;
+
+ if (port) {
+ nl = (struct Nodelist *)HashLookup(port->net, &Nodehash);
+ if (doLoadBalance && (nl != NULL)) {
+ if ((nl->ignore == FALSE) && (nl->ratio > 1.0)) {
+ if (VerboseFlag)
+ printf("\nGate %s (%s) should be %g times stronger",
+ inst->instname, inst->cellname, nl->ratio);
+ needscorrecting = TRUE;
+ orig = gl->suffix;
+ glbest = best_size(gl, nl->total_load + WireCap, NULL);
+ if (glbest && VerboseFlag)
+ printf("\nGate changed from %s to %s\n", gl->gatename,
+ glbest->gatename);
+ inv_size = nl->total_load;
+ }
+
+ // Is this node an output pin? Check required output drive.
+ if ((nl->ignore == FALSE) && (nl->type == OUTPUTPIN)) {
+ orig = gl->suffix;
+ glbest = best_size(gl, nl->total_load + MaxOutputCap
+ + WireCap, NULL);
+ if (glbest && (glbest != gl)) {
+ if (doLoadBalance) {
+ needscorrecting = TRUE;
+ if (VerboseFlag)
+ printf("\nOutput Gate changed from %s to %s\n",
+ gl->gatename, glbest->gatename);
+ }
+ }
+ }
+ // Don't attempt to correct gates for which we cannot
+ // find a suffix
+ if (orig == NULL) needscorrecting = FALSE;
+ }
+ }
+
+ /* Write cell name, possibly modified for gate strength */
+ if (needscorrecting) {
+ if (glbest == NULL) { // return val to insert inverters
+
+ if (VerboseFlag)
+ printf("\nInsert buffers %s - %g\n", s, inv_size);
+
+ s = strstr(port->name, nl->nodename); // get output node
+ s = strtok(s, " \\\t"); // strip it clean
+ if (*s == '[') {
+ char *p = strrchr(s, ']');
+ if (p != NULL)
+ strcpy(p, "_bF$buf]\";\n"); // rename it
+ else
+ strcat(s, "_bF%buf\";\n");
+ }
+ else
+ strcat(s, "_bF$buf\";\n"); // rename it
+
+ bbest = best_size(glbuf, inv_size + WireCap, NULL);
+
+ /* If bbest->suffix is NULL, then we will have to break */
+ /* up this network. */
+ /* Buffer trees will be inserted by downstream tools, */
+ /* after analyzing the placement of the network. This */
+ /* error needs to be passed down to those tools. . . */
+
+ if (bbest == NULL) {
+ fprintf(stderr, "Fatal error: No gates found for %s\n",
+ glbuf->gatename);
+ }
+
+ dl = (struct Drivelist *)HashLookup(bbest->suffix, &Drivehash);
+ if (dl != NULL) dl->NgatesOut++;
+
+ /* Recompute size of the gate driving the buffer */
+ if (nl != NULL) {
+ get_pincap(bbest->gatecell, buf_in_pin, &nl->total_load);
+ if (gl != NULL) {
+ nl->total_load += gl->Cint;
+ }
+ }
+ orig = gl->suffix;
+ glbest = best_size(gl, nl->total_load + WireCap, NULL);
+
+ /* Prepend buffer to instance list */
+ newinst = PrependInstance(topcell, bbest->gatename);
+ sprintf(instname, "%s_insert%d", bbest->gatename, cidx);
+ newinst->instname = strdup(instname);
+ newport = InstPort(newinst, buf_in_pin, s);
+ newport = InstPort(newinst, buf_out_pin, nl->nodename);
+ cidx++;
+
+ /* Register the new node name */
+ if (HashLookup(s, &topcell->nets) == NULL) {
+ Net(topcell, s);
+ registernode(s, INPUT, bbest, buf_in_pin);
+ }
+ }
+ if ((gl != NULL) && (gl != glbest)) Changed_count++;
+
+ /* Reassign the instance's cell */
+ free(inst->cellname);
+ inst->cellname = strdup(glbest->gatename);
+
+ /* Adjust the gate count for "in" and "out" types */
+ count_gatetype(gl, 0, -1);
+ count_gatetype(glbest, 0, 1);
+ }
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Rewrite the verilog output with resized gates and clock and buffer trees
+ *---------------------------------------------------------------------------
+ */
+
+void write_output(struct cellrec *topcell, FILE *outfptr, int doLoadBalance,
+ int doFanout)
+{
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+
+ /* Write output module header */
+ fprintf(outfptr, "/* Verilog module written by vlogFanout (qflow) */\n");
+ if (doFanout)
+ fprintf(outfptr, "/* With clock tree generation and fanout reduction */\n");
+ if (doLoadBalance)
+ fprintf(outfptr, "/* %s gate resizing */\n", (doFanout) ? "and" : "With");
+ fprintf(outfptr, "\n");
+
+ fprintf(outfptr, "module %s(\n", topcell->name);
+ for (port = topcell->portlist; port; port = port->next) {
+ switch(port->direction) {
+ case PORT_INPUT:
+ fprintf(outfptr, " input ");
+ break;
+ case PORT_OUTPUT:
+ fprintf(outfptr, " output ");
+ break;
+ case PORT_INOUT:
+ fprintf(outfptr, " inout ");
+ break;
+ }
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ fprintf(outfptr, "[%d:%d] ", net->start, net->end);
+ }
+ fprintf(outfptr, "%s", port->name);
+ if (port->next) fprintf(outfptr, ",");
+ fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, ");\n\n");
+
+ /* Declare all wires */
+ RecurseHashTablePointer(&topcell->nets, output_wires, outfptr);
+ fprintf(outfptr, "\n");
+
+ /* Write instances in the order of the input file */
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ int nprops = RecurseHashTable(&inst->propdict, CountHashTableEntries);
+ fprintf(outfptr, "%s ", inst->cellname);
+ if (nprops > 0) {
+ fprintf(outfptr, "#(\n");
+ RecurseHashTablePointer(&inst->propdict, output_props, outfptr);
+ fprintf(outfptr, ") ");
+ }
+ if (inst->cellname)
+ fprintf(outfptr, "%s (\n", inst->instname);
+ else
+ fprintf(outfptr, "vlogFanout: No cell for instance %s\n", inst->instname);
+
+ /* Write each port and net connection */
+ for (port = inst->portlist; port; port = port->next) {
+ fprintf(outfptr, " .%s(%s)", port->name, port->net);
+ if (port->next) fprintf(outfptr, ",");
+ fprintf(outfptr, "\n");
+ }
+ fprintf(outfptr, ");\n\n");
+ }
+
+ /* End the module */
+ fprintf(outfptr, "endmodule\n");
+
+ fflush(stdout);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * Return a pointer to the gate with the drive strength that is the
+ * minimum necessary to drive the load "amount".
+ *
+ * If the load exceeds the available gate sizes, then return the maximum
+ * strength gate available, and set "overload" to TRUE. Otherwise, FALSE
+ * is returned in "overload".
+ *---------------------------------------------------------------------------
+ */
+
+struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload)
+{
+ char *s;
+ char ssave;
+ int ind, i;
+ double amax = 1.0E10; // Assuming no gate is this big!
+ double gmax = 0.0;
+ struct Gatelist *newgl, *glbest = NULL, *glsave = NULL;
+ struct Baselist *bl;
+
+ if (overload) *overload = FALSE;
+ if ((s = gl->suffix) == NULL) return NULL;
+ ind = (int)(s - gl->gatename); // Compare out to and including the suffix
+ ssave = gl->gatename[ind];
+ gl->gatename[ind] = '\0';
+
+ bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash);
+ gl->gatename[ind] = ssave;
+
+ for (i = 0; bl && (i < bl->Ndrives); i++) {
+ newgl = bl->gates[i];
+ if (newgl->strength >= gmax) {
+ gmax = newgl->strength;
+ glsave = newgl;
+ }
+ if (amount <= newgl->strength) {
+ if (newgl->strength < amax) {
+ if (newgl->suffix) {
+ glbest = newgl;
+ amax = newgl->strength;
+ }
+ }
+ }
+ }
+
+ if (amax == 1.0E10) {
+ double oratio;
+
+ stren_err_counter++;
+ if (overload) *overload = TRUE;
+
+ if (glsave != NULL)
+ glbest = glsave;
+ else
+ glbest = NULL;
+
+ if (gmax > 0.0) {
+ oratio = (double)(amount / gmax);
+ if (oratio > MaxOverload) {
+
+ fprintf(stderr, "Warning %d: load of %g is %g times greater "
+ "than strongest gate %s\n",
+ stren_err_counter, amount, oratio, glsave->gatename);
+
+ if (MaxOverload == 0.0)
+ fprintf(stderr, "This warning will only be repeated for "
+ "larger overload ratios. Warning count reflects\n"
+ "the total number of overloaded nets.\n");
+
+ MaxOverload = oratio;
+ }
+ }
+ }
+ return glbest;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *---------------------------------------------------------------------------
+ */
+
+void helpmessage(void)
+{
+ printf("\nvlogFanout:\n\n");
+ printf("vlogFanout looks at a synthesized BLIF netlist.\n");
+ printf("Node fanout is measured, and gate size is adjusted.\n");
+ printf("File \"gate.cfg\" is used to describe the RTL gates.\n\n");
+
+ printf("\tUsage: vlogFanout [-switches] vlog_in [vlog_out].\n\n");
+
+ printf("vlogFanout returns the number of gate substitutions made.\n");
+ printf("Typically, it will be iterated until convergence (return value 0).\n\n");
+
+ printf("valid switches are:\n");
+ printf("\t-f\t\tRun gate fanout buffering only (no load balancing)\n");
+ printf("\t-L\t\tRun gate load balance optimization only (no fanout buffering)\n");
+ printf("\t-g\t\tDebug mode: parse and print the gate.cfg table\n");
+ printf("\t-n\t\tDebug mode: parse and print the node list\n");
+ printf("\t-v\t\tDebug mode: verbose output\n");
+ printf("\t-l latency\tSet the maximum variable latency (ps). "
+ "(value %g, default 1000.0)\n", MaxLatency);
+ printf("\t-F value\tSet the maximum fanout per node (value %d, default 16)\n",
+ MaxFanout);
+ printf("\t-b buffername\tSet the name of a buffer gate\n");
+ printf("\t-i pin_name\tSet the name of the buffer gate input pin (used with -b)\n");
+ printf("\t-o pin_name\tSet the name of the buffer gate output pin (used with -b)\n");
+ printf("\t-s separator\tGate names have \"separator\" before drive strength\n");
+ printf("\t-c value\tSet the maximum output capacitance (fF). "
+ "(value %g, default 30.0)\n", MaxOutputCap);
+ printf("\t-p filepath\tSpecify an alternate path and filename for gate.cfg\n");
+ printf("\t-I filepath\tSpecify a path and filename for list of nets to ignore\n");
+ printf("\t-h\t\tprint this help message\n\n");
+
+ printf("This will not work at all for tristate gates.\n");
+ printf("Nodes with multiple outputs are assumed to be in parallel.\n");
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * main routine for vlogFanout
+ *---------------------------------------------------------------------------
+ */
+
+int main (int argc, char *argv[])
+{
+ int i, j, k, l, iter, cidx;
+ int state;
+ int maxline, curline;
+ int libcount;
+ int inputcount;
+ int gateinputs;
+ int gatecount;
+ int doLoadBalance = 1;
+ int doFanout = 1;
+ int nodetype, porttype;
+ char netname[MAXLINE];
+ char *pinname;
+ char *libfile, *libsep;
+ char *separg = NULL;
+ char *test;
+ char *s, *t, *comptr;
+ FILE *outfptr;
+ char *line;
+ GateRec *Gatepaths = NULL;
+ struct Gatelist *gl = NULL;
+ struct Nodelist *nl = NULL, *nlmax, *nlimax;
+ struct Drivelist *dl = NULL;
+ struct cellrec *topcell;
+ struct portrec *port;
+ struct instance *inst;
+ struct netrec *net;
+ int cur_pintype;
+
+ SuffixIsNumeric = TRUE; // By default, assume numeric suffixes
+
+ InitializeHashTable(&Nodehash, LARGEHASHSIZE);
+ InitializeHashTable(&Bushash, SMALLHASHSIZE);
+ InitializeHashTable(&Drivehash, SMALLHASHSIZE);
+ InitializeHashTable(&Gatehash, SMALLHASHSIZE);
+ InitializeHashTable(&Basehash, SMALLHASHSIZE);
+
+ fprintf(stdout, "vlogFanout for qflow " QFLOW_VERSION "." QFLOW_REVISION "\n");
+
+ while ((i = getopt(argc, argv, "fLSgnhvl:c:b:i:o:p:s:I:F:")) != EOF) {
+ switch (i) {
+ case 'b':
+ /* If value is a comma-separated pair, the first is a */
+ /* general purpose buffer and the second is a clock buffer. */
+ Buffername = strdup(optarg);
+ if ((comptr = strchr(Buffername, ',')) != NULL) {
+ *comptr = '\0';
+ Clkbufname = comptr + 1;
+ }
+ break;
+ case 'i':
+ buf_in_pin = strdup(optarg);
+ if ((comptr = strchr(buf_in_pin, ',')) != NULL) {
+ *comptr = '\0';
+ clkbuf_in_pin = comptr + 1;
+ }
+ break;
+ case 'o':
+ buf_out_pin = strdup(optarg);
+ if ((comptr = strchr(buf_out_pin, ',')) != NULL) {
+ *comptr = '\0';
+ clkbuf_out_pin = comptr + 1;
+ }
+ break;
+ case 'p':
+ /* Allow multiple files to be specified as space-separated */
+ /* list, either by specifying all on one "-p" arguments */
+ /* or by passing multiple "-p" arguments. */
+
+ if (Gatepaths == NULL) {
+ libcount = 1;
+ Gatepaths = (GateRec *)malloc(sizeof(GateRec));
+ Gatepaths->path = strdup(optarg);
+ Gatepaths->sep = (separg) ? strdup(separg) : NULL;
+ }
+ else {
+ libcount++;
+ Gatepaths = (GateRec *)realloc(Gatepaths, libcount * sizeof(GateRec));
+ Gatepaths[libcount - 1].path = strdup(optarg);
+ Gatepaths[libcount - 1].sep = (separg) ? strdup(separg) : NULL;
+ }
+ break;
+ case 'f': // fanout only
+ doLoadBalance = 0;
+ break;
+ case 'L': // load balance only
+ doFanout = 0;
+ break;
+ case 'I':
+ Ignorepath = strdup(optarg);
+ break;
+ case 'F':
+ MaxFanout = atoi(optarg);
+ break;
+ case 'l':
+ MaxLatency = atof(optarg);
+ break;
+ case 'c':
+ MaxOutputCap = atof(optarg);
+ break;
+ case 's':
+ if (!strcasecmp(optarg, "none")) {
+ if (separg) free(separg);
+ separg = NULL;
+ }
+ else if (!strcasecmp(optarg, "nullstring")) {
+ if (separg) free(separg);
+ separg = strdup("");
+ }
+ else
+ separg = strdup(optarg);
+ break;
+ case 'S':
+ if (separg) free(separg);
+ separg = NULL;
+ break;
+ case 'g':
+ GatePrintFlag = 1;
+ break;
+ case 'n':
+ NodePrintFlag = 1;
+ break;
+ case 'v':
+ VerboseFlag = 1;
+ break;
+ case 'h':
+ helpmessage();
+ return 3;
+ break;
+ default:
+ break;
+ }
+ }
+ if (separg) free(separg);
+
+ /* If there is only one set of in and out pins, then assume */
+ /* that the pin names apply to both regular and clock */
+ /* buffer types. */
+
+ if (clkbuf_in_pin == NULL) {
+ clkbuf_in_pin = buf_in_pin;
+ }
+ if (clkbuf_out_pin == NULL) {
+ clkbuf_out_pin = buf_out_pin;
+ }
+
+ Inputfname = Outputfname = NULL;
+ outfptr = stdout;
+ i = optind;
+
+ if (i < argc) {
+ Inputfname = strdup(argv[i]);
+ }
+ i++;
+ if (i < argc) {
+ Outputfname = strdup(argv[i]);
+ if (!(outfptr = fopen(Outputfname, "w"))) {
+ fprintf(stderr, "vlogFanout: Couldn't open %s for writing.\n", Outputfname);
+ return 1;
+ }
+ }
+ i++;
+
+ // Make sure we have a valid gate file path
+ if (Gatepaths == NULL) {
+ fprintf(stderr, "vlogFanout: No liberty file(s) specified.\n");
+ return 1;
+ }
+ gatecount = 0;
+ for (l = 0; l < libcount; l++) {
+ int loccount;
+ libfile = Gatepaths[l].path;
+ libsep = Gatepaths[l].sep;
+ loccount = read_gate_file(libfile, libsep);
+ if (loccount == 0)
+ fprintf(stderr, "vlogFanout: Warning: No gates found in file %s!\n",
+ libfile);
+ gatecount += loccount;
+ }
+
+ // Determine if suffix is numeric or alphabetic
+ if (gatecount > 0) {
+ char *suffix;
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
+ while (gl && gl->suffix == NULL)
+ gl = (struct Gatelist *)HashNext(&Gatehash);
+ if (gl && gl->suffix && !isdigit(*gl->suffix))
+ SuffixIsNumeric = FALSE;
+ }
+
+ if (gatecount == 0) {
+ fprintf(stderr, "vlogFanout: No gates found in any input file!\n");
+ return 1;
+ }
+ if (GatePrintFlag) {
+ showgatelist();
+ return 0;
+ }
+
+ if (buf_in_pin == NULL || buf_out_pin == NULL) {
+ Pin *curpin;
+
+ gl = (struct Gatelist *)NULL;
+
+ if (Buffername != NULL) {
+ gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
+ if (gl == NULL) {
+ fprintf(stderr, "No buffer \"%s\" found in gate list\n", Buffername);
+ fprintf(stderr, "Searching gate list for suitable buffer.\n");
+ }
+ }
+
+ if ((gl == NULL) || (Buffername == NULL)) {
+ // Find a suitable buffer
+ gl = (struct Gatelist *)HashFirst(&Gatehash);
+ while (gl != NULL) {
+ Cell *ctest;
+
+ // Find the first gate with one input, one output,
+ // and a function string that matches the input pin name.
+
+ ctest = gl->gatecell;
+ if (ctest->pins && ctest->pins->next && !ctest->pins->next->next) {
+ if (ctest->pins->type == PIN_INPUT &&
+ ctest->pins->next->type == PIN_OUTPUT) {
+ if (is_buffer_func(ctest->function, ctest->pins->name,
+ ctest->pins->next->name)) {
+ fprintf(stdout, "Using cell \"%s\" for buffers.\n",
+ ctest->name);
+ Buffername = strdup(ctest->name);
+ break;
+ }
+ }
+ else if (ctest->pins->type == PIN_OUTPUT &&
+ ctest->pins->next->type == PIN_INPUT) {
+ if (is_buffer_func(ctest->function, ctest->pins->next->name,
+ ctest->pins->name)) {
+ fprintf(stdout, "Using cell \"%s\" for buffers.\n",
+ ctest->name);
+ Buffername = strdup(ctest->name);
+ break;
+ }
+ }
+ }
+ gl = (struct Gatelist *)HashNext(&Gatehash);
+ }
+ }
+ else
+ gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash);
+
+ if (gl == NULL) {
+ if (Buffername == NULL)
+ fprintf(stderr, "vlogFanout: No suitable buffer cell in library.\n");
+ else
+ fprintf(stderr, "vlogFanout: Buffer cell %s cannot be found.\n",
+ Buffername);
+ return 1;
+ }
+ for (curpin = gl->gatecell->pins; curpin; curpin = curpin->next) {
+ if (curpin->type == PIN_INPUT) {
+ if (buf_in_pin == NULL)
+ buf_in_pin = strdup(curpin->name);
+ }
+ else if (curpin->type == PIN_OUTPUT) {
+ if (buf_out_pin == NULL)
+ buf_out_pin = strdup(curpin->name);
+ }
+ }
+ if (buf_in_pin == NULL || buf_out_pin == NULL) {
+ fprintf(stderr, "vlogFanout: Could not parse I/O pins "
+ "of buffer cell %s.\n", Buffername);
+ return 1;
+ }
+ }
+
+ /* If Clkbufname is not defined, make it the same as Buffername */
+ if (Clkbufname == NULL) Clkbufname = Buffername;
+
+ /* Read the verilog file */
+ topcell = ReadVerilog(Inputfname);
+
+ if (topcell == NULL) {
+ fprintf(stderr, "vlogFanout: No module found in file!\n");
+ return 1;
+ }
+
+ /* Transfer the contents of the verilog top cell into the local database */
+
+ for (port = topcell->portlist; port; port = port->next) {
+ int locdir;
+
+ // Translate port direction from definitions in readverilog.h to
+ // the list of direction definitions in "enum nodetype_" above.
+
+ switch (port->direction) {
+ case PORT_INPUT:
+ locdir = INPUTPIN;
+ break;
+ case PORT_OUTPUT:
+ locdir = OUTPUTPIN;
+ break;
+ case PORT_INOUT:
+ locdir = INOUTPIN;
+ break;
+ default:
+ locdir = UNKNOWN;
+ break;
+ }
+
+ /* Find net corresponding to the port name and bit blast if needed */
+
+ net = BusHashLookup(port->name, &topcell->nets);
+ if (net->start > net->end) {
+ for (i = net->end; i <= net->start; i++) {
+ sprintf(netname, "%s[%d]", port->name, i);
+ registernode(netname, locdir, NULL, NULL);
+ }
+ }
+ else if (net->start < net->end) {
+ for (i = net->start; i <= net->end; i++) {
+ sprintf(netname, "%s[%d]", port->name, i);
+ registernode(netname, locdir, NULL, NULL);
+ }
+ }
+ else {
+ registernode(port->name, locdir, NULL, NULL);
+ }
+ }
+
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ /* Match the gate record pulled from verilog to the gate list */
+ /* pulled from the liberty file. */
+
+ if (!inst->cellname) {
+ fprintf(stderr, "Error: Instance %s does not name a corresponding cell!\n",
+ inst->instname);
+ continue;
+ }
+ gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash);
+ if (gl != NULL) {
+ for (port = inst->portlist; port; port = port->next) {
+ cur_pintype = get_pintype(gl->gatecell, port->name);
+ switch(cur_pintype) {
+ case PIN_OUTPUT:
+ /* Gate output */
+ nodetype = OUTPUT;
+ porttype = PORT_OUTPUT;
+ break;
+ case PIN_INPUT:
+ /* Gate input */
+ nodetype = INPUT;
+ porttype = PORT_INPUT;
+ break;
+ case PIN_CLOCK:
+ /* Gate clock */
+ nodetype = CLOCK;
+ porttype = PORT_INPUT;
+ break;
+ default:
+ /* Unknown */
+ nodetype = UNKNOWN;
+ porttype = PORT_NONE;
+ break;
+ }
+ registernode(port->net, nodetype, gl, port->name);
+ port->direction = porttype;
+ }
+ }
+ }
+
+ /* get list of nets to ignore, if there is one, and mark nets to ignore */
+ if (Ignorepath != NULL) read_ignore_file(Ignorepath);
+
+ if (NodePrintFlag) {
+ shownodes();
+ return 0;
+ }
+
+ /* Apply iterative buffering and resizing */
+
+ iter = 0;
+ cidx = 0;
+ Changed_count = 1;
+ while (Changed_count > 0) {
+ iter++;
+
+ /* Show top fanout gate */
+ nlmax = NULL;
+ nlimax = NULL;
+ nl = (struct Nodelist *)HashFirst(&Nodehash);
+ while (nl != NULL) {
+ if (nl->outputgatestrength != 0.0) {
+ nl->ratio = nl->total_load / nl->outputgatestrength;
+ }
+ if (nl->ignore == FALSE) {
+ if ((nl->num_inputs >= Topfanout) && (nl->outputgatestrength != 0.0)) {
+ Topfanout = nl->num_inputs;
+ nlmax = nl;
+ }
+ else if ((nl->num_inputs >= Inputfanout) && (nl->type == INPUTPIN)) {
+ Inputfanout = nl->num_inputs;
+ nlimax = nl;
+ }
+ if ((nl->ratio >= Topratio) && (nl->outputgatestrength != 0.0)) {
+ Topratio = nl->ratio;
+ }
+ if ((nl->total_load >= Topload) && (nl->outputgatestrength != 0.0)) {
+ Topload = nl->total_load;
+ }
+ else if ((nl->total_load >= Inputload) && (nl->type == INPUTPIN)) {
+ Inputload = nl->total_load;
+ }
+ }
+ nl = (struct Nodelist *)HashNext(&Nodehash);
+ }
+
+ if (VerboseFlag) printf("\nIteration %d\n", iter);
+ fflush(stdout);
+
+ if (nlmax) {
+ fprintf(stderr, "Top internal fanout is %d (load %g) from node %s,\n"
+ "driven by %s with strength %g (fF driven at latency %g)\n",
+ Topfanout, Topload, nlmax->nodename,
+ nlmax->outputgate->gatename,
+ nlmax->outputgatestrength,
+ MaxLatency);
+
+ fprintf(stderr, "Top fanout load-to-strength ratio is %g (latency = %g ps)\n",
+ Topratio, MaxLatency * Topratio);
+
+ fprintf(stderr, "Top input node fanout is %d (load %g) from node %s.\n",
+ Inputfanout, Inputload, nlimax->nodename);
+ }
+
+ fprintf(stderr, "%d gates exceed specified minimum load.\n", stren_err_counter);
+
+ if (doFanout) cidx = insert_buffers(topcell, cidx);
+ fprintf(stderr, "%d buffers were added.\n", Buffer_count);
+
+ resize_gates(topcell, doLoadBalance, doFanout);
+ fprintf(stderr, "%d gates were changed.\n", Changed_count);
+
+ fprintf(stderr, "\nGate counts by drive strength:\n\n");
+ dl = (struct Drivelist *)HashFirst(&Drivehash);
+ while (dl != NULL) {
+ if (dl->NgatesIn > 0) {
+ fprintf(stderr, "\t\"%s%s\" gates\tIn: %d \tOut: %d \t%+d\n",
+ dl->Separator, dl->DriveType, dl->NgatesIn,
+ dl->NgatesOut, (dl->NgatesOut - dl->NgatesIn));
+ }
+ dl = (struct Drivelist *)HashNext(&Drivehash);
+ }
+ fprintf(stderr, "\n");
+ }
+
+ write_output(topcell, outfptr, doLoadBalance, doFanout);
+ if (outfptr != stdout) fclose(outfptr);
+
+ // Output number of gates changed so we can iterate until this is zero.
+
+ fprintf(stdout, "Number of gates changed: %d\n", Changed_count + Buffer_count);
+ return 0;
+}
+
+/* end of vlogFanout.c */
diff --git a/tech/Makefile.in b/tech/Makefile.in
index 73b02a6..1cfbf91 100644
--- a/tech/Makefile.in
+++ b/tech/Makefile.in
@@ -11,7 +11,7 @@ INSTALL = @INSTALL@
QFLOW_LIB_DIR = /usr/share/qflow
-TECH_DIRS = osu050 osu035 osu018
+TECH_DIRS = osu050 osu035 osu035_redm4 osu018
TECHINSTALL = ${QFLOW_LIB_DIR}/tech
diff --git a/tech/osu035_redm4/Makefile.in b/tech/osu035_redm4/Makefile.in
new file mode 100644
index 0000000..b117edd
--- /dev/null
+++ b/tech/osu035_redm4/Makefile.in
@@ -0,0 +1,42 @@
+#
+# qflow project included technology osu035_redm4 files
+#
+
+# Main compiler arguments
+CFLAGS = @CFLAGS@
+DEFS = @DEFS@
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+INSTALL = @INSTALL@
+
+QFLOW_LIB_DIR = @QFLOW_LIB_DIR@
+
+TECH_FILES = osu035_redm4.par osu035_redm4.sh osu035_redm4.magicrc osu035.prm
+TECH_FILES += osu035_redm4_stdcells.lef osu035_stdcells.sp osu035_stdcells.lib
+TECH_FILES += osu035_stdcells.v osu035_redm4_setup.tcl osu035_stdcells.gds2
+
+TECHINSTALL = ${QFLOW_LIB_DIR}/tech
+
+# Substitute the target qflow tech directory name in .magicrc so that magic
+# can find the OSU035_REDM4 techfile
+
+all: osu035_redm4.magicrc
+
+install: ${TECH_FILES}
+ @echo "Installing osu035_redm4 tech files"
+ $(INSTALL) -d $(DESTDIR)$(TECHINSTALL)/osu035_redm4
+ for target in $(TECH_FILES); do \
+ $(INSTALL) $$target $(DESTDIR)$(TECHINSTALL)/osu035_redm4 ;\
+ done
+
+osu035_redm4.magicrc: osu035_redm4.magicrc.in Makefile
+ sed < $< -e '/QFLOW_LIB_DIR/s#QFLOW_LIB_DIR#$(QFLOW_LIB_DIR)#' > $@
+
+clean:
+ $(RM) -f osu035_redm4.magicrc
+
+distclean:
+ $(RM) -f osu035_redm4.magicrc
+
+uninstall:
+
diff --git a/tech/osu035_redm4/osu035.prm b/tech/osu035_redm4/osu035.prm
new file mode 120000
index 0000000..6087983
--- /dev/null
+++ b/tech/osu035_redm4/osu035.prm
@@ -0,0 +1 @@
+../osu035/osu035.prm \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_redm4.magicrc.in b/tech/osu035_redm4/osu035_redm4.magicrc.in
new file mode 120000
index 0000000..cfab051
--- /dev/null
+++ b/tech/osu035_redm4/osu035_redm4.magicrc.in
@@ -0,0 +1 @@
+../osu035/osu035.magicrc.in \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_redm4.par b/tech/osu035_redm4/osu035_redm4.par
new file mode 120000
index 0000000..ddcc518
--- /dev/null
+++ b/tech/osu035_redm4/osu035_redm4.par
@@ -0,0 +1 @@
+../osu035/osu035.par \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_redm4.sh b/tech/osu035_redm4/osu035_redm4.sh
new file mode 100644
index 0000000..09261a4
--- /dev/null
+++ b/tech/osu035_redm4/osu035_redm4.sh
@@ -0,0 +1,77 @@
+#!/bin/tcsh
+#---------------------------------------------------------------
+# Shell script setting up all variables used by the qflow scripts
+# for this project
+#---------------------------------------------------------------
+
+# The LEF file containing standard cell macros
+
+set leffile=osu035_redm4_stdcells.lef
+
+# The SPICE netlist containing subcell definitions for all the standard cells
+
+set spicefile=osu035_stdcells.sp
+
+# The liberty format file containing standard cell timing and function information
+
+set libertyfile=osu035_stdcells.lib
+
+# If there is another LEF file containing technology information
+# that is separate from the file containing standard cell macros,
+# set this. Otherwise, leave it defined as an empty string.
+
+set techleffile=""
+
+# All cells below should be the lowest output drive strength value,
+# if the standard cell set has multiple cells with different drive
+# strengths. Comment out any cells that do not exist.
+
+set flopcell=DFFPOSX1 ;# Standard positive-clocked DFF, no set or reset
+# set flopset=DFFS ;# DFF with preset, if available
+# set flopreset=DFFSR ;# DFF with clear, if available
+set flopsetreset=DFFSR ;# DFF with both set and clear
+set setpin=S ;# The name of the set pin on DFFs
+set resetpin=R ;# The name of the clear/reset pin on DFFs
+set setpininvert=1 ;# Set this to 1 if the set pin is inverted (!set)
+set resetpininvert=1 ;# Set this to 1 if the reset pin is inverted (!reset)
+set floppinout=Q ;# Name of the output pin on DFFs
+set floppinin=D ;# Name of the output pin on DFFs
+set floppinclk=CLK ;# Name of the clock pin on DFFs
+
+set bufcell=BUFX2 ;# Minimum drive strength buffer cell
+set bufpin_in=A ;# Name of input port to buffer cell
+set bufpin_out=Y ;# Name of output port to buffer cell
+set clkbufcell=CLKBUF1 ;# Minimum drive strength clock buffer cell
+set clkbufpin_in=A ;# Name of input port to clock buffer cell
+set clkbufpin_out=Y ;# Name of output port to clock buffer cell
+set inverter=INVX1 ;# Minimum drive strength inverter cell
+set invertpin_in=A ;# Name of input port to inverter cell
+set invertpin_out=Y ;# Name of output port to inverter cell
+set norgate=NOR2X1 ;# 2-input NOR gate, minimum drive strength
+set norpin_in1=A ;# Name of first input pin to NOR gate
+set norpin_in2=B ;# Name of second input pin to NOR gate
+set norpin_out=Y ;# Name of output pin from OR gate
+set nandgate=NAND2X1 ;# 2-input NAND gate, minimum drive strength
+set nandpin_in1=A ;# Name of first input pin to NAND gate
+set nandpin_in2=B ;# Name of second input pin to NAND gate
+set nandpin_out=Y ;# Name of output pin from NAND gate
+set fillcell=FILL ;# Spacer (filler) cell (may use regexp)
+set decapcell="" ;# Decap (filler) cell (may use regexp)
+set antennacell="" ;# Antenna (filler) cell (may use regexp)
+set antennapin_in="" ;# Input pin name of antennta cell, if it exists
+
+set tiehi="" ;# Cell to connect to power, if one exists
+set tiehipin_out="" ;# Output pin name of tiehi cell, if it exists
+set tielo="" ;# Cell to connect to ground, if one exists
+set tielopin_out="" ;# Output pin name of tielo cell, if it exists
+
+set separator="" ;# Separator between gate names and drive strengths
+set techfile=SCN4M_SUBM.20 ;# magic techfile
+set magicrc=osu035_redm4.magicrc ;# magic startup script
+set gdsfile=osu035_stdcells.gds2 ;# GDS database of standard cells
+
+# Option defaults
+set fanout_options="-l 200 -c 30" ;# blifFanout target maximum latency
+ ;# per gate 200ps, output load set to 50fF
+set vesta_options="--summary reports --long"
+set addspacers_options="-stripe 5 150 PG"
diff --git a/tech/osu035_redm4/osu035_redm4_setup.tcl b/tech/osu035_redm4/osu035_redm4_setup.tcl
new file mode 120000
index 0000000..9c0ccff
--- /dev/null
+++ b/tech/osu035_redm4/osu035_redm4_setup.tcl
@@ -0,0 +1 @@
+../osu035/osu035_setup.tcl \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_redm4_stdcells.lef b/tech/osu035_redm4/osu035_redm4_stdcells.lef
new file mode 100644
index 0000000..ed161b4
--- /dev/null
+++ b/tech/osu035_redm4/osu035_redm4_stdcells.lef
@@ -0,0 +1,3179 @@
+# LEF file generated by Abstract Generator version 5.5.10 on Jul 30 14:47:58 2004
+#
+# Contains LEF for all bins.
+# Options: [x] Antenna
+# [x] Geometry
+# [x] Technology
+
+VERSION 5.4 ;
+NAMESCASESENSITIVE ON ;
+BUSBITCHARS "[]" ;
+DIVIDERCHAR "/" ;
+UNITS
+ DATABASE MICRONS 1000 ;
+END UNITS
+
+USEMINSPACING OBS ON ;
+USEMINSPACING PIN OFF ;
+CLEARANCEMEASURE EUCLIDEAN ;
+
+
+MANUFACTURINGGRID 0.1 ;
+
+LAYER nwell
+ TYPE MASTERSLICE ;
+END nwell
+
+LAYER nactive
+ TYPE MASTERSLICE ;
+END nactive
+
+LAYER pactive
+ TYPE MASTERSLICE ;
+END pactive
+
+LAYER poly
+ TYPE MASTERSLICE ;
+END poly
+
+LAYER cc
+ TYPE CUT ;
+ SPACING 0.9 ;
+END cc
+
+LAYER metal1
+ TYPE ROUTING ;
+ DIRECTION HORIZONTAL ;
+ PITCH 2 ;
+ OFFSET 1 ;
+ WIDTH 0.6 ;
+ SPACING 0.6 ;
+ RESISTANCE RPERSQ 0.07 ;
+ CAPACITANCE CPERSQDIST 3e-05 ;
+END metal1
+
+LAYER via1
+ TYPE CUT ;
+ SPACING 0.6 ;
+END via1
+
+LAYER metal2
+ TYPE ROUTING ;
+ DIRECTION VERTICAL ;
+ PITCH 1.6 ;
+ OFFSET 0.8 ;
+ WIDTH 0.6 ;
+ SPACING 0.6 ;
+ RESISTANCE RPERSQ 0.07 ;
+ CAPACITANCE CPERSQDIST 1.7e-05 ;
+END metal2
+
+LAYER via2
+ TYPE CUT ;
+ SPACING 0.6 ;
+END via2
+
+LAYER metal3
+ TYPE ROUTING ;
+ DIRECTION HORIZONTAL ;
+ PITCH 2 ;
+ OFFSET 1 ;
+ WIDTH 0.6 ;
+ SPACING 0.6 ;
+ RESISTANCE RPERSQ 0.07 ;
+ CAPACITANCE CPERSQDIST 7e-06 ;
+END metal3
+
+LAYER via3
+ TYPE CUT ;
+ SPACING 0.8 ;
+END via3
+
+LAYER metal4
+ TYPE ROUTING ;
+ DIRECTION VERTICAL ;
+ PITCH 1.6 ;
+ OFFSET 0.8 ;
+ WIDTH 0.6 ;
+ SPACING 0.6 ;
+ RESISTANCE RPERSQ 0.04 ;
+ CAPACITANCE CPERSQDIST 4e-06 ;
+END metal4
+
+VIA M2_M1 DEFAULT
+ LAYER metal1 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+ LAYER via1 ;
+ RECT -0.200 -0.200 0.200 0.200 ;
+ LAYER metal2 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+END M2_M1
+
+VIA M3_M2 DEFAULT
+ LAYER metal2 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+ LAYER via2 ;
+ RECT -0.200 -0.200 0.200 0.200 ;
+ LAYER metal3 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+END M3_M2
+
+VIA M4_M3 DEFAULT
+ LAYER metal3 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+ LAYER via3 ;
+ RECT -0.200 -0.200 0.200 0.200 ;
+ LAYER metal4 ;
+ RECT -0.400 -0.400 0.400 0.400 ;
+END M4_M3
+
+
+VIARULE viagen21 GENERATE
+ LAYER metal1 ;
+ DIRECTION HORIZONTAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.2 ;
+ METALOVERHANG 0 ;
+ LAYER metal2 ;
+ DIRECTION VERTICAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.2 ;
+ METALOVERHANG 0 ;
+ LAYER via1 ;
+ RECT -0.2 -0.2 0.2 0.2 ;
+ SPACING 1 BY 1 ;
+END viagen21
+
+VIARULE viagen32 GENERATE
+ LAYER metal3 ;
+ DIRECTION HORIZONTAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.2 ;
+ METALOVERHANG 0 ;
+ LAYER metal2 ;
+ DIRECTION VERTICAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.2 ;
+ METALOVERHANG 0 ;
+ LAYER via2 ;
+ RECT -0.2 -0.2 0.2 0.2 ;
+ SPACING 1 BY 1 ;
+END viagen32
+
+VIARULE viagen43 GENERATE
+ LAYER metal3 ;
+ DIRECTION HORIZONTAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.4 ;
+ METALOVERHANG 0 ;
+ LAYER metal4 ;
+ DIRECTION VERTICAL ;
+ WIDTH 0.6 TO 60 ;
+ OVERHANG 0.4 ;
+ METALOVERHANG 0 ;
+ LAYER via3 ;
+ RECT -0.2 -0.2 0.2 0.2 ;
+ SPACING 1.2 BY 1.2 ;
+END viagen43
+
+VIARULE TURN1 GENERATE
+ LAYER metal1 ;
+ DIRECTION HORIZONTAL ;
+ LAYER metal1 ;
+ DIRECTION VERTICAL ;
+END TURN1
+
+VIARULE TURN2 GENERATE
+ LAYER metal2 ;
+ DIRECTION HORIZONTAL ;
+ LAYER metal2 ;
+ DIRECTION VERTICAL ;
+END TURN2
+
+VIARULE TURN3 GENERATE
+ LAYER metal3 ;
+ DIRECTION HORIZONTAL ;
+ LAYER metal3 ;
+ DIRECTION VERTICAL ;
+END TURN3
+
+VIARULE TURN4 GENERATE
+ LAYER metal4 ;
+ DIRECTION HORIZONTAL ;
+ LAYER metal4 ;
+ DIRECTION VERTICAL ;
+END TURN4
+
+SITE corner
+ CLASS PAD ;
+ SYMMETRY R90 Y ;
+ SIZE 300.000 BY 300.000 ;
+END corner
+
+SITE IO
+ CLASS PAD ;
+ SYMMETRY Y ;
+ SIZE 90.000 BY 300.000 ;
+END IO
+
+SITE core
+ CLASS CORE ;
+ SYMMETRY Y ;
+ SIZE 1.600 BY 20.000 ;
+END core
+
+MACRO FILL
+ CLASS CORE ;
+ FOREIGN FILL 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 1.600 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT -0.400 -0.600 2.000 0.600 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT -0.400 19.400 2.000 20.600 ;
+ END
+ END vdd
+END FILL
+
+MACRO AND2X1
+ CLASS CORE ;
+ FOREIGN AND2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 8.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.600 9.800 3.400 11.400 ;
+ RECT 2.000 10.600 3.400 11.400 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.000 -0.600 3.800 5.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 12.600 6.000 13.400 ;
+ RECT 5.200 14.800 6.000 18.800 ;
+ RECT 5.400 3.200 6.000 18.800 ;
+ RECT 4.600 1.200 5.400 3.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ RECT 3.600 14.800 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 5.200 ;
+ RECT 0.600 5.200 2.400 5.800 ;
+ RECT 1.800 5.200 2.400 6.600 ;
+ RECT 4.000 5.800 4.800 6.600 ;
+ RECT 1.800 6.000 4.800 6.600 ;
+ RECT 4.000 5.800 4.600 14.200 ;
+ RECT 2.200 13.600 4.600 14.200 ;
+ RECT 2.200 13.600 2.800 18.800 ;
+ RECT 2.000 14.800 2.800 18.800 ;
+ END
+END AND2X1
+
+MACRO AND2X2
+ CLASS CORE ;
+ FOREIGN AND2X2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 8.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 7.200 2.800 9.400 ;
+ RECT 2.400 7.000 3.200 7.800 ;
+ RECT 2.000 8.600 2.800 9.400 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.000 -0.600 3.800 5.000 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ RECT 5.200 10.800 6.000 18.800 ;
+ RECT 5.400 4.200 6.000 18.800 ;
+ RECT 4.600 1.200 5.400 5.200 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ RECT 3.600 11.200 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 5.200 ;
+ RECT 0.600 1.200 1.200 6.000 ;
+ RECT 0.600 5.400 2.400 6.000 ;
+ RECT 1.800 5.800 4.800 6.400 ;
+ RECT 4.000 5.800 4.800 6.600 ;
+ RECT 4.000 5.800 4.600 10.600 ;
+ RECT 2.200 10.000 4.600 10.600 ;
+ RECT 2.200 10.000 2.800 18.800 ;
+ RECT 2.000 14.800 2.800 18.800 ;
+ END
+END AND2X2
+
+MACRO AOI21X1
+ CLASS CORE ;
+ FOREIGN AOI21X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 8.600 1.200 9.400 ;
+ RECT 1.200 8.800 2.000 9.800 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 6.600 2.800 8.200 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.000 3.800 5.800 4.600 ;
+ RECT 5.200 4.600 6.000 5.400 ;
+ END
+ END C
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 1.000 -0.600 1.800 5.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ RECT 5.200 -0.600 6.000 3.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 1.200 4.200 9.400 ;
+ RECT 5.200 10.800 6.000 18.800 ;
+ RECT 3.600 8.800 6.000 9.400 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ RECT 5.200 8.600 5.800 18.800 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.000 2.800 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 10.800 4.400 11.400 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ END
+END AOI21X1
+
+MACRO AOI22X1
+ CLASS CORE ;
+ FOREIGN AOI22X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 8.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 8.600 1.200 9.400 ;
+ RECT 1.200 8.800 2.000 9.800 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 6.600 2.800 8.200 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 6.800 8.600 7.600 10.200 ;
+ END
+ END C
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 6.600 5.800 8.600 ;
+ RECT 5.200 6.600 6.000 7.400 ;
+ RECT 5.000 7.800 5.800 8.600 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.800 -0.600 1.600 5.200 ;
+ RECT -0.400 -0.600 8.400 0.600 ;
+ RECT 6.800 -0.600 7.600 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 8.600 4.400 9.400 ;
+ RECT 5.200 10.800 6.000 17.600 ;
+ RECT 5.200 9.600 5.800 17.600 ;
+ RECT 3.800 9.600 5.800 10.200 ;
+ RECT 3.400 1.200 5.000 5.200 ;
+ RECT 3.800 1.200 4.400 10.200 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.000 2.800 20.600 ;
+ RECT -0.400 19.400 8.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 10.800 4.400 11.400 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ RECT 6.800 10.800 7.600 18.800 ;
+ RECT 3.600 18.200 7.600 18.800 ;
+ END
+END AOI22X1
+
+MACRO BUFX2
+ CLASS CORE ;
+ FOREIGN BUFX2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 4.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 7.800 1.200 9.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.200 ;
+ RECT -0.400 -0.600 5.200 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 1.200 4.400 8.600 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ RECT 3.800 1.200 4.400 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.000 2.800 20.600 ;
+ RECT -0.400 19.400 5.200 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 6.400 ;
+ RECT 0.400 5.800 2.600 6.400 ;
+ RECT 2.000 9.400 3.200 10.200 ;
+ RECT 2.000 5.800 2.600 11.400 ;
+ RECT 0.400 10.800 2.600 11.400 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ END
+END BUFX2
+
+MACRO BUFX4
+ CLASS CORE ;
+ FOREIGN BUFX4 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.600 7.800 1.400 9.400 ;
+ RECT 0.400 8.600 1.400 9.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ RECT 5.200 -0.600 6.000 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ RECT 4.000 4.600 4.600 11.800 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ RECT 3.600 6.600 4.600 7.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.000 2.800 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ RECT 5.200 10.800 6.000 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 6.400 ;
+ RECT 0.400 5.800 3.000 6.400 ;
+ RECT 2.400 8.000 3.400 8.800 ;
+ RECT 2.400 5.800 3.000 11.400 ;
+ RECT 0.400 10.800 3.000 11.400 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ END
+END BUFX4
+
+MACRO DFFNEGX1
+ CLASS CORE ;
+ FOREIGN DFFNEGX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 19.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN Q
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 14.600 9.400 15.400 10.200 ;
+ RECT 18.000 1.200 18.800 18.800 ;
+ RECT 14.600 9.600 18.800 10.200 ;
+ RECT 15.000 5.600 18.800 6.200 ;
+ RECT 15.000 5.400 15.800 6.200 ;
+ END
+ END Q
+ PIN CLK
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 5.200 6.800 6.000 13.400 ;
+ LAYER via1 ;
+ RECT 5.400 12.800 5.800 13.200 ;
+ RECT 5.400 7.000 5.800 7.400 ;
+ LAYER metal1 ;
+ RECT 5.200 12.600 6.000 13.400 ;
+ RECT 1.200 6.800 12.800 7.400 ;
+ RECT 12.000 6.600 12.800 7.400 ;
+ RECT 5.200 6.800 6.000 7.600 ;
+ RECT 4.200 4.600 5.000 5.400 ;
+ RECT 4.000 5.400 4.800 7.400 ;
+ RECT 1.200 6.600 2.800 7.400 ;
+ RECT 5.400 13.400 6.200 14.200 ;
+ END
+ END CLK
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.800 8.400 3.600 9.200 ;
+ RECT 6.800 8.600 7.600 9.400 ;
+ RECT 2.800 8.600 7.600 9.200 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.200 ;
+ RECT -0.400 -0.600 19.600 0.600 ;
+ RECT 16.400 -0.600 17.200 5.000 ;
+ RECT 10.800 -0.600 11.600 3.200 ;
+ RECT 7.400 -0.600 8.400 3.200 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 11.000 2.800 20.600 ;
+ RECT -0.400 19.400 19.600 20.600 ;
+ RECT 16.400 10.800 17.200 20.600 ;
+ RECT 10.800 14.800 11.600 20.600 ;
+ RECT 7.600 14.800 8.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 0.400 5.200 1.200 10.800 ;
+ RECT 3.600 3.200 4.400 10.000 ;
+ RECT 3.600 3.200 4.200 14.800 ;
+ RECT 3.600 10.800 4.400 14.800 ;
+ RECT 13.200 3.200 14.000 11.400 ;
+ RECT 13.200 3.200 13.800 14.800 ;
+ RECT 13.200 12.200 14.000 14.800 ;
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 6.000 ;
+ RECT 3.600 14.000 4.400 15.400 ;
+ RECT 3.600 14.800 5.600 15.400 ;
+ RECT 4.800 14.800 5.600 18.800 ;
+ RECT 4.800 1.200 5.600 3.200 ;
+ RECT 3.600 2.600 5.600 3.200 ;
+ RECT 3.600 2.600 4.400 4.000 ;
+ RECT 3.600 11.400 9.000 12.000 ;
+ RECT 3.600 11.400 4.400 12.200 ;
+ RECT 8.200 11.400 9.000 12.200 ;
+ RECT 9.200 1.200 10.000 3.200 ;
+ RECT 9.200 1.200 9.800 4.400 ;
+ RECT 7.000 3.800 9.800 4.400 ;
+ RECT 7.000 3.800 7.800 4.600 ;
+ RECT 7.000 13.400 7.800 14.200 ;
+ RECT 9.800 13.400 10.600 14.200 ;
+ RECT 7.000 13.600 10.600 14.200 ;
+ RECT 9.200 13.600 9.800 18.800 ;
+ RECT 9.200 14.800 10.000 18.800 ;
+ RECT 13.200 14.000 14.000 14.800 ;
+ RECT 13.400 14.800 14.600 18.800 ;
+ RECT 0.400 9.800 5.000 10.400 ;
+ RECT 4.200 10.200 11.400 10.800 ;
+ RECT 10.800 10.200 11.400 12.200 ;
+ RECT 10.800 11.400 14.600 12.000 ;
+ RECT 10.800 11.400 11.800 12.200 ;
+ RECT 13.800 11.400 14.600 12.200 ;
+ RECT 0.400 9.800 1.200 18.800 ;
+ RECT 13.400 1.200 14.600 3.200 ;
+ RECT 13.200 2.600 14.000 4.000 ;
+ RECT 13.200 8.000 14.000 8.800 ;
+ RECT 13.200 8.200 17.000 8.800 ;
+ RECT 16.200 8.200 17.000 9.000 ;
+ LAYER via1 ;
+ RECT 0.600 10.200 1.000 10.600 ;
+ RECT 0.600 5.400 1.000 5.800 ;
+ RECT 3.800 14.200 4.200 14.600 ;
+ RECT 3.800 11.600 4.200 12.000 ;
+ RECT 3.800 3.400 4.200 3.800 ;
+ RECT 13.400 14.200 13.800 14.600 ;
+ RECT 13.400 8.200 13.800 8.600 ;
+ RECT 13.400 3.400 13.800 3.800 ;
+ END
+END DFFNEGX1
+
+MACRO NOR3X1
+ CLASS CORE ;
+ FOREIGN NOR3X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 12.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 4.600 3.800 5.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 6.600 5.200 7.400 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 8.600 6.800 9.400 ;
+ END
+ END C
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 3.200 ;
+ RECT -0.400 -0.600 13.200 0.600 ;
+ RECT 5.200 -0.600 6.000 2.800 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 1.200 4.400 3.200 ;
+ RECT 10.000 12.000 10.800 17.600 ;
+ RECT 10.000 10.600 10.800 11.400 ;
+ RECT 10.000 10.600 10.600 17.600 ;
+ RECT 7.400 10.600 10.800 11.200 ;
+ RECT 7.400 3.200 8.000 11.200 ;
+ RECT 6.800 1.200 7.600 4.000 ;
+ RECT 4.000 3.400 8.000 4.000 ;
+ RECT 4.000 2.600 4.600 4.000 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.800 2.800 20.600 ;
+ RECT -0.400 19.400 13.200 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.600 11.600 4.200 12.200 ;
+ RECT 3.600 11.600 4.200 18.800 ;
+ RECT 3.600 12.800 4.400 18.800 ;
+ RECT 0.600 11.600 1.200 18.800 ;
+ RECT 0.400 12.800 1.200 18.800 ;
+ RECT 6.800 13.000 7.600 18.800 ;
+ RECT 3.600 18.200 7.600 18.800 ;
+ RECT 5.400 11.800 9.000 12.400 ;
+ RECT 5.400 11.800 6.000 17.600 ;
+ RECT 5.200 12.800 6.000 17.600 ;
+ RECT 8.400 12.000 9.200 18.000 ;
+ RECT 11.600 12.000 12.400 18.000 ;
+ RECT 8.600 12.000 9.200 18.800 ;
+ RECT 11.600 12.000 12.200 18.800 ;
+ RECT 8.600 18.200 12.200 18.800 ;
+ END
+END NOR3X1
+
+MACRO DFFPOSX1
+ CLASS CORE ;
+ FOREIGN DFFPOSX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 19.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN Q
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 14.600 9.400 15.400 10.200 ;
+ RECT 18.000 1.200 18.800 18.800 ;
+ RECT 14.600 9.600 18.800 10.200 ;
+ RECT 15.000 5.600 18.800 6.200 ;
+ RECT 15.000 5.400 15.800 6.200 ;
+ END
+ END Q
+ PIN CLK
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 1.200 6.600 2.800 7.400 ;
+ RECT 13.400 12.200 14.800 13.000 ;
+ RECT 13.400 10.600 14.000 13.000 ;
+ RECT 11.600 10.600 14.000 11.200 ;
+ RECT 11.600 6.800 12.200 11.200 ;
+ RECT 11.000 6.600 11.800 7.400 ;
+ RECT 1.200 6.800 12.200 7.400 ;
+ RECT 5.400 3.800 6.000 7.400 ;
+ RECT 5.200 3.800 6.000 4.600 ;
+ RECT 4.200 6.800 5.000 7.600 ;
+ END
+ END CLK
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.600 8.400 3.400 9.200 ;
+ RECT 6.800 8.600 7.600 9.400 ;
+ RECT 2.600 8.600 7.600 9.200 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.200 ;
+ RECT -0.400 -0.600 19.600 0.600 ;
+ RECT 16.400 -0.600 17.200 5.000 ;
+ RECT 10.800 -0.600 11.600 3.200 ;
+ RECT 7.400 -0.600 8.400 3.200 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 11.000 2.800 20.600 ;
+ RECT -0.400 19.400 19.600 20.600 ;
+ RECT 16.400 10.800 17.200 20.600 ;
+ RECT 10.800 14.800 11.600 20.600 ;
+ RECT 7.600 14.800 8.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 0.400 5.200 1.200 10.800 ;
+ RECT 3.600 3.200 4.400 14.800 ;
+ RECT 13.200 3.200 14.000 14.800 ;
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 6.000 ;
+ RECT 3.600 14.000 4.400 15.400 ;
+ RECT 3.600 14.800 5.600 15.400 ;
+ RECT 4.800 14.800 5.600 18.800 ;
+ RECT 4.800 1.200 5.600 3.200 ;
+ RECT 3.600 2.600 5.600 3.200 ;
+ RECT 3.600 2.600 4.400 4.000 ;
+ RECT 3.600 11.400 9.000 12.000 ;
+ RECT 3.600 11.400 4.400 12.200 ;
+ RECT 8.200 11.400 9.000 12.200 ;
+ RECT 9.200 1.200 10.000 3.200 ;
+ RECT 9.200 1.200 9.800 4.400 ;
+ RECT 7.000 3.800 9.800 4.400 ;
+ RECT 7.000 3.800 7.800 4.600 ;
+ RECT 7.000 13.400 7.800 14.200 ;
+ RECT 9.800 13.400 10.600 14.200 ;
+ RECT 7.000 13.600 10.600 14.200 ;
+ RECT 9.200 13.600 9.800 18.800 ;
+ RECT 9.200 14.800 10.000 18.800 ;
+ RECT 0.400 10.000 6.200 10.400 ;
+ RECT 0.400 9.800 6.000 10.400 ;
+ RECT 5.400 10.200 10.200 10.800 ;
+ RECT 9.600 10.200 10.200 12.600 ;
+ RECT 11.000 11.800 11.800 12.600 ;
+ RECT 9.600 12.000 11.800 12.600 ;
+ RECT 0.400 9.800 1.200 18.800 ;
+ RECT 13.200 14.000 14.000 14.800 ;
+ RECT 13.400 14.800 14.600 18.800 ;
+ RECT 13.400 1.200 14.600 3.200 ;
+ RECT 13.200 2.600 14.000 4.000 ;
+ RECT 13.200 8.000 14.000 8.800 ;
+ RECT 13.200 8.200 17.000 8.800 ;
+ RECT 16.200 8.200 17.000 9.000 ;
+ LAYER via1 ;
+ RECT 0.600 10.200 1.000 10.600 ;
+ RECT 0.600 5.400 1.000 5.800 ;
+ RECT 3.800 14.200 4.200 14.600 ;
+ RECT 3.800 11.600 4.200 12.000 ;
+ RECT 3.800 3.400 4.200 3.800 ;
+ RECT 13.400 14.200 13.800 14.600 ;
+ RECT 13.400 8.200 13.800 8.600 ;
+ RECT 13.400 3.400 13.800 3.800 ;
+ END
+END DFFPOSX1
+
+MACRO FAX1
+ CLASS CORE ;
+ FOREIGN FAX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 24.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN YC
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 22.800 1.200 23.600 3.200 ;
+ RECT 22.800 14.800 23.600 18.800 ;
+ RECT 23.000 1.200 23.600 18.800 ;
+ RECT 22.800 6.600 23.600 7.400 ;
+ END
+ END YC
+ PIN YS
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 19.600 1.200 20.400 3.200 ;
+ RECT 20.800 4.600 22.000 5.400 ;
+ RECT 19.400 9.200 21.400 9.800 ;
+ RECT 20.800 3.800 21.400 9.800 ;
+ RECT 19.800 3.800 21.400 4.400 ;
+ RECT 19.600 14.800 20.400 18.800 ;
+ RECT 19.800 1.200 20.400 4.400 ;
+ RECT 19.400 9.200 20.000 15.400 ;
+ END
+ END YS
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.600 6.000 2.000 6.600 ;
+ RECT 17.800 6.800 18.600 7.600 ;
+ RECT 17.800 5.600 18.400 7.600 ;
+ RECT 7.400 5.600 18.400 6.200 ;
+ RECT 0.600 6.000 8.200 6.400 ;
+ RECT 1.200 5.800 18.400 6.200 ;
+ RECT 0.400 6.600 1.200 7.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 7.200 2.800 9.400 ;
+ RECT 16.000 6.800 16.800 7.600 ;
+ RECT 9.400 6.800 16.800 7.400 ;
+ RECT 2.200 7.200 10.200 7.600 ;
+ RECT 2.800 7.000 16.800 7.400 ;
+ RECT 5.800 7.000 6.600 7.800 ;
+ RECT 2.200 7.200 3.600 7.800 ;
+ RECT 2.000 8.600 2.800 9.400 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 8.600 5.200 9.400 ;
+ RECT 14.400 8.000 15.200 8.800 ;
+ RECT 10.400 8.200 15.200 8.800 ;
+ RECT 3.600 8.600 11.800 9.000 ;
+ RECT 3.600 8.600 11.000 9.200 ;
+ END
+ END C
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 4.000 ;
+ RECT -0.400 -0.600 24.400 0.600 ;
+ RECT 21.200 -0.600 22.000 3.200 ;
+ RECT 18.000 -0.600 18.800 5.000 ;
+ RECT 11.000 -0.600 11.800 3.800 ;
+ RECT 7.800 -0.600 8.600 4.800 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.000 2.800 20.600 ;
+ RECT -0.400 19.400 24.400 20.600 ;
+ RECT 21.200 14.800 22.000 20.600 ;
+ RECT 18.000 9.200 18.800 20.600 ;
+ RECT 11.000 12.800 11.800 20.600 ;
+ RECT 7.800 10.800 8.600 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 14.400 4.200 15.200 5.000 ;
+ RECT 14.600 7.800 20.200 8.400 ;
+ RECT 19.400 7.800 20.200 8.600 ;
+ RECT 14.600 4.200 15.200 10.200 ;
+ RECT 14.400 9.400 15.200 10.200 ;
+ RECT 5.200 4.400 6.000 5.200 ;
+ RECT 12.000 9.600 12.800 11.400 ;
+ RECT 20.800 10.600 21.600 11.400 ;
+ RECT 5.200 10.800 21.600 11.400 ;
+ RECT 5.400 4.400 6.000 11.600 ;
+ RECT 5.200 10.800 6.000 11.600 ;
+ LAYER metal1 ;
+ RECT 0.400 10.800 4.400 11.400 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ RECT 0.400 1.200 1.200 5.200 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ RECT 0.400 4.600 4.400 5.200 ;
+ RECT 5.200 10.800 6.000 18.800 ;
+ RECT 5.200 1.200 6.000 5.200 ;
+ RECT 9.400 11.600 13.400 12.200 ;
+ RECT 9.400 10.800 10.200 18.800 ;
+ RECT 12.600 11.600 13.400 18.800 ;
+ RECT 9.400 1.200 10.200 5.000 ;
+ RECT 12.600 1.200 13.400 5.000 ;
+ RECT 9.400 4.400 13.400 5.000 ;
+ RECT 12.800 9.400 13.600 10.200 ;
+ RECT 12.000 9.600 12.800 10.400 ;
+ RECT 14.400 9.400 15.200 18.800 ;
+ RECT 14.200 10.200 15.200 18.800 ;
+ RECT 14.200 1.200 15.200 4.200 ;
+ RECT 14.400 1.200 15.200 5.000 ;
+ RECT 19.400 7.000 20.200 8.600 ;
+ RECT 20.800 10.600 22.400 11.400 ;
+ LAYER via1 ;
+ RECT 5.400 11.000 5.800 11.400 ;
+ RECT 5.400 4.600 5.800 5.000 ;
+ RECT 12.200 9.800 12.600 10.200 ;
+ RECT 14.600 9.600 15.000 10.000 ;
+ RECT 14.600 4.400 15.000 4.800 ;
+ RECT 19.600 8.000 20.000 8.400 ;
+ RECT 21.000 10.800 21.400 11.200 ;
+ END
+END FAX1
+
+MACRO HAX1
+ CLASS CORE ;
+ FOREIGN HAX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 16.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN YC
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 4.600 3.200 6.000 4.000 ;
+ RECT 5.200 8.400 6.000 9.200 ;
+ RECT 5.400 3.200 6.000 9.200 ;
+ LAYER via1 ;
+ RECT 4.800 3.400 5.200 3.800 ;
+ RECT 5.400 8.600 5.800 9.000 ;
+ LAYER metal1 ;
+ RECT 4.600 1.200 5.400 4.000 ;
+ RECT 5.200 8.400 6.000 18.800 ;
+ END
+ END YC
+ PIN YS
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 13.200 10.600 14.000 11.400 ;
+ RECT 14.200 14.800 15.000 18.800 ;
+ RECT 14.400 13.600 15.000 18.800 ;
+ RECT 13.400 4.000 15.000 4.600 ;
+ RECT 14.400 1.200 15.000 4.600 ;
+ RECT 13.400 13.600 15.000 14.200 ;
+ RECT 14.200 1.200 15.000 3.200 ;
+ RECT 13.400 4.000 14.000 14.200 ;
+ END
+ END YS
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.000 1.200 7.400 ;
+ RECT 10.000 6.600 10.800 7.400 ;
+ RECT 8.400 6.600 10.800 7.200 ;
+ RECT 0.400 6.000 9.000 6.600 ;
+ RECT 0.800 5.800 1.600 6.600 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 8.000 3.000 8.600 ;
+ RECT 8.400 7.800 9.200 8.600 ;
+ RECT 7.200 7.800 9.200 8.400 ;
+ RECT 2.400 7.200 7.800 7.800 ;
+ RECT 2.400 7.200 3.200 8.000 ;
+ RECT 2.000 8.600 2.800 9.400 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 16.400 0.600 ;
+ RECT 12.600 -0.600 13.400 3.200 ;
+ RECT 6.200 -0.600 7.000 5.000 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 15.200 2.800 20.600 ;
+ RECT -0.400 19.400 16.400 20.600 ;
+ RECT 12.600 14.800 13.400 20.600 ;
+ RECT 11.000 10.800 11.800 20.600 ;
+ RECT 6.800 14.800 7.600 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 3.800 4.600 4.600 5.400 ;
+ RECT 3.800 4.600 4.400 9.200 ;
+ RECT 3.800 8.400 4.600 9.200 ;
+ LAYER metal1 ;
+ RECT 3.800 8.400 4.600 9.200 ;
+ RECT 0.600 14.000 4.400 14.600 ;
+ RECT 0.600 14.000 1.200 18.800 ;
+ RECT 0.400 14.800 1.200 18.800 ;
+ RECT 3.800 8.400 4.400 18.800 ;
+ RECT 3.600 14.000 4.400 18.800 ;
+ RECT 3.000 1.200 3.800 5.200 ;
+ RECT 3.800 4.600 5.600 5.400 ;
+ RECT 7.800 1.200 11.800 1.800 ;
+ RECT 11.000 1.200 11.800 4.800 ;
+ RECT 7.800 1.200 8.600 5.200 ;
+ RECT 9.400 2.400 10.200 5.200 ;
+ RECT 9.600 2.400 10.200 6.000 ;
+ RECT 9.600 5.400 12.600 6.000 ;
+ RECT 11.400 5.400 12.600 6.200 ;
+ RECT 11.400 5.400 12.000 10.200 ;
+ RECT 8.600 9.600 12.000 10.200 ;
+ RECT 8.600 9.600 9.200 18.800 ;
+ RECT 8.400 10.800 9.200 18.800 ;
+ LAYER via1 ;
+ RECT 4.000 8.600 4.400 9.000 ;
+ RECT 4.000 4.800 4.400 5.200 ;
+ END
+END HAX1
+
+MACRO INVX1
+ CLASS CORE ;
+ FOREIGN INVX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 3.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 3.800 1.200 5.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 3.200 ;
+ RECT -0.400 -0.600 3.600 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 3.600 20.600 ;
+ END
+ END vdd
+END INVX1
+
+MACRO INVX2
+ CLASS CORE ;
+ FOREIGN INVX2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 3.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 5.800 1.200 7.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 3.600 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 3.600 20.600 ;
+ END
+ END vdd
+END INVX2
+
+MACRO INVX4
+ CLASS CORE ;
+ FOREIGN INVX4 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 4.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 5.800 1.200 7.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 5.200 0.600 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 5.200 20.600 ;
+ RECT 3.600 10.800 4.400 20.600 ;
+ END
+ END vdd
+END INVX4
+
+MACRO INVX8
+ CLASS CORE ;
+ FOREIGN INVX8 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 8.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 5.800 1.200 7.400 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 8.400 0.600 ;
+ RECT 6.800 -0.600 7.600 5.200 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 6.600 ;
+ RECT 5.200 1.200 6.000 18.800 ;
+ RECT 2.000 9.400 6.000 10.200 ;
+ RECT 2.000 5.800 6.000 6.600 ;
+ RECT 2.000 9.400 2.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 8.400 20.600 ;
+ RECT 6.800 10.800 7.600 20.600 ;
+ RECT 3.600 10.800 4.400 20.600 ;
+ END
+ END vdd
+END INVX8
+
+MACRO NAND2X1
+ CLASS CORE ;
+ FOREIGN NAND2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 4.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 5.800 1.200 7.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 10.600 4.400 12.200 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 5.200 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 4.600 2.800 18.800 ;
+ RECT 2.000 4.600 3.800 5.200 ;
+ RECT 3.000 1.200 3.800 5.200 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 5.200 20.600 ;
+ RECT 3.600 14.800 4.400 20.600 ;
+ END
+ END vdd
+END NAND2X1
+
+MACRO NAND3X1
+ CLASS CORE ;
+ FOREIGN NAND3X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 9.800 1.200 11.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 8.600 3.600 9.400 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 11.800 4.400 13.400 ;
+ END
+ END C
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 7.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 14.000 2.800 18.800 ;
+ RECT 5.200 14.800 6.000 18.800 ;
+ RECT 5.200 10.600 6.000 11.400 ;
+ RECT 5.200 6.800 5.800 18.800 ;
+ RECT 2.200 14.000 5.800 14.600 ;
+ RECT 4.200 6.800 5.800 7.400 ;
+ RECT 4.000 1.200 4.800 7.200 ;
+ RECT 2.000 14.800 2.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ RECT 3.600 15.200 4.400 20.600 ;
+ END
+ END vdd
+END NAND3X1
+
+MACRO NOR2X1
+ CLASS CORE ;
+ FOREIGN NOR2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 4.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 3.800 1.200 5.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 8.600 4.400 10.200 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 3.200 ;
+ RECT -0.400 -0.600 5.200 0.600 ;
+ RECT 3.600 -0.600 4.400 3.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 3.200 ;
+ RECT 3.000 10.800 3.800 18.800 ;
+ RECT 2.000 10.800 3.800 11.600 ;
+ RECT 2.200 1.200 2.800 11.600 ;
+ RECT 2.000 6.600 2.800 7.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 5.200 20.600 ;
+ END
+ END vdd
+END NOR2X1
+
+MACRO OAI21X1
+ CLASS CORE ;
+ FOREIGN OAI21X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 7.400 ;
+ RECT 1.200 6.200 2.000 7.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 7.800 2.800 9.400 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 4.600 10.800 5.200 13.400 ;
+ RECT 5.200 10.600 6.000 11.400 ;
+ RECT 4.400 12.600 5.200 13.400 ;
+ END
+ END C
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 4.400 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.000 10.800 3.800 18.800 ;
+ RECT 3.400 6.600 6.000 7.400 ;
+ RECT 5.200 1.200 6.000 5.200 ;
+ RECT 5.200 1.200 5.800 7.400 ;
+ RECT 3.400 6.600 4.000 11.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ RECT 4.600 14.800 5.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 5.200 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ RECT 0.600 5.000 4.200 5.600 ;
+ END
+END OAI21X1
+
+MACRO OAI22X1
+ CLASS CORE ;
+ FOREIGN OAI22X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 8.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 7.400 ;
+ RECT 1.200 6.200 2.000 7.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 7.800 2.800 9.400 ;
+ END
+ END B
+ PIN C
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 6.800 6.600 7.600 8.200 ;
+ END
+ END C
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 7.800 6.000 9.400 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 4.400 ;
+ RECT -0.400 -0.600 8.400 0.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 6.600 4.200 18.800 ;
+ RECT 3.600 6.600 6.000 7.200 ;
+ RECT 5.400 2.400 6.000 7.200 ;
+ RECT 5.200 2.400 6.000 5.200 ;
+ RECT 3.000 10.800 5.000 18.800 ;
+ RECT 3.600 6.600 4.400 7.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 8.400 20.600 ;
+ RECT 6.800 10.800 7.600 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 3.600 1.200 7.600 1.800 ;
+ RECT 0.400 1.200 1.200 5.200 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ RECT 6.800 1.200 7.600 5.200 ;
+ RECT 0.600 5.000 4.200 5.600 ;
+ END
+END OAI22X1
+
+MACRO OR2X1
+ CLASS CORE ;
+ FOREIGN OR2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 3.800 1.200 5.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 6.600 2.800 7.400 ;
+ RECT 2.200 5.800 3.600 6.600 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 3.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ RECT 3.600 -0.600 4.400 3.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 1.200 6.000 3.200 ;
+ RECT 5.400 1.200 6.000 14.800 ;
+ RECT 4.600 14.800 5.400 18.800 ;
+ RECT 4.800 14.200 6.000 14.800 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.000 10.800 3.800 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 3.200 ;
+ RECT 2.200 1.200 2.800 4.800 ;
+ RECT 2.200 4.200 4.800 4.800 ;
+ RECT 4.200 4.200 4.800 7.800 ;
+ RECT 3.800 7.200 4.400 10.200 ;
+ RECT 3.800 9.400 4.600 10.200 ;
+ RECT 0.400 9.600 4.600 10.200 ;
+ RECT 0.400 9.600 1.200 18.800 ;
+ END
+END OR2X1
+
+MACRO OR2X2
+ CLASS CORE ;
+ FOREIGN OR2X2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 6.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 3.800 1.200 5.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 6.600 3.000 7.400 ;
+ RECT 2.400 7.400 3.200 8.200 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 3.200 ;
+ RECT -0.400 -0.600 6.800 0.600 ;
+ RECT 3.600 -0.600 4.400 4.800 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 1.200 6.000 5.200 ;
+ RECT 5.400 1.200 6.000 11.400 ;
+ RECT 4.600 10.800 5.400 18.800 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.000 10.800 3.800 20.600 ;
+ RECT -0.400 19.400 6.800 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 3.200 ;
+ RECT 2.200 1.200 2.800 6.000 ;
+ RECT 2.200 5.400 4.600 6.000 ;
+ RECT 3.800 9.000 4.600 9.800 ;
+ RECT 4.000 5.400 4.600 9.800 ;
+ RECT 0.400 9.600 4.400 10.200 ;
+ RECT 0.400 9.600 1.200 18.800 ;
+ END
+END OR2X2
+
+MACRO TBUFX1
+ CLASS CORE ;
+ FOREIGN TBUFX1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 8.000 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 6.000 6.600 7.600 7.400 ;
+ END
+ END A
+ PIN EN
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 12.600 2.000 13.400 ;
+ END
+ END EN
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 3.200 ;
+ RECT -0.400 -0.600 8.400 0.600 ;
+ RECT 6.400 -0.600 7.200 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.800 1.200 4.600 5.200 ;
+ RECT 3.800 10.800 4.600 18.800 ;
+ RECT 4.000 1.200 4.600 18.800 ;
+ RECT 3.600 8.600 4.600 9.400 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 8.400 20.600 ;
+ RECT 6.400 10.800 7.200 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 3.200 ;
+ RECT 2.600 6.600 3.400 7.400 ;
+ RECT 2.600 2.400 3.200 8.000 ;
+ RECT 2.400 7.400 3.000 10.600 ;
+ RECT 2.600 10.000 3.200 15.400 ;
+ RECT 2.000 14.800 2.800 18.800 ;
+ END
+END TBUFX1
+
+MACRO TBUFX2
+ CLASS CORE ;
+ FOREIGN TBUFX2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 11.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 9.000 6.600 10.800 7.400 ;
+ END
+ END A
+ PIN EN
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 5.800 1.000 10.200 ;
+ RECT 0.400 5.800 1.400 6.600 ;
+ RECT 0.400 8.600 1.200 10.200 ;
+ END
+ END EN
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 11.600 0.600 ;
+ RECT 8.400 -0.600 9.200 4.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 2.400 5.800 17.600 ;
+ RECT 5.200 10.800 6.000 17.600 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ RECT 5.200 2.400 6.000 5.200 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 11.600 20.600 ;
+ RECT 8.400 12.200 9.200 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 5.200 ;
+ RECT 2.000 8.200 2.800 9.000 ;
+ RECT 2.000 1.200 2.600 18.800 ;
+ RECT 2.000 10.800 2.800 18.800 ;
+ RECT 6.800 10.800 10.800 11.600 ;
+ RECT 3.600 10.800 4.400 18.800 ;
+ RECT 6.800 10.800 7.600 18.800 ;
+ RECT 3.600 18.200 7.600 18.800 ;
+ RECT 10.000 10.800 10.800 18.800 ;
+ RECT 3.600 1.200 7.600 1.800 ;
+ RECT 10.000 1.200 10.800 4.600 ;
+ RECT 6.800 1.200 7.600 5.800 ;
+ RECT 3.600 1.200 4.400 5.200 ;
+ RECT 10.200 1.200 10.800 5.800 ;
+ RECT 6.800 5.200 10.800 5.800 ;
+ END
+END TBUFX2
+
+MACRO XOR2X1
+ CLASS CORE ;
+ FOREIGN XOR2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 11.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 2.000 7.400 ;
+ RECT 4.000 7.000 4.800 7.800 ;
+ RECT 2.000 7.000 4.800 7.600 ;
+ RECT 0.400 6.800 2.600 7.400 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 9.200 6.600 10.800 7.400 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 -0.600 3.000 4.600 ;
+ RECT -0.400 -0.600 11.600 0.600 ;
+ RECT 8.200 -0.600 9.200 4.600 ;
+ RECT 2.000 1.200 3.000 4.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 5.200 8.600 6.000 9.400 ;
+ RECT 4.800 10.800 6.400 18.800 ;
+ RECT 5.800 1.200 6.400 7.400 ;
+ RECT 5.400 6.800 6.000 18.800 ;
+ RECT 4.800 1.200 6.400 4.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.200 3.000 18.800 ;
+ RECT -0.400 19.400 11.600 20.600 ;
+ RECT 8.200 12.200 9.200 20.600 ;
+ RECT 2.200 12.200 3.000 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 2.200 5.200 3.000 6.000 ;
+ RECT 2.200 5.400 7.800 6.000 ;
+ RECT 3.600 5.400 4.400 6.200 ;
+ RECT 7.000 5.400 7.800 6.200 ;
+ RECT 2.200 5.200 2.800 11.600 ;
+ RECT 2.200 10.800 3.000 11.600 ;
+ RECT 8.400 5.200 9.200 6.000 ;
+ RECT 3.600 6.800 9.200 7.400 ;
+ RECT 3.600 6.800 4.200 9.600 ;
+ RECT 3.400 8.800 4.200 9.600 ;
+ RECT 8.600 5.200 9.200 11.600 ;
+ RECT 8.400 10.800 9.200 11.600 ;
+ LAYER metal1 ;
+ RECT 0.400 10.800 3.000 11.400 ;
+ RECT 2.200 10.800 3.000 11.600 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ RECT 0.400 1.200 1.200 5.800 ;
+ RECT 0.400 5.200 3.000 5.800 ;
+ RECT 2.200 5.200 3.000 6.000 ;
+ RECT 2.600 8.600 3.400 9.400 ;
+ RECT 3.400 8.800 4.200 9.600 ;
+ RECT 3.600 5.400 5.200 6.200 ;
+ RECT 7.000 5.400 7.800 6.200 ;
+ RECT 7.200 5.400 7.800 7.400 ;
+ RECT 7.200 6.600 8.000 7.400 ;
+ RECT 8.400 10.800 10.800 11.400 ;
+ RECT 8.400 10.800 9.200 11.600 ;
+ RECT 10.000 10.800 10.800 18.800 ;
+ RECT 10.000 1.200 10.800 5.800 ;
+ RECT 8.400 5.200 10.800 5.800 ;
+ RECT 8.400 5.200 9.200 6.000 ;
+ LAYER via1 ;
+ RECT 2.400 11.000 2.800 11.400 ;
+ RECT 2.400 5.400 2.800 5.800 ;
+ RECT 3.600 9.000 4.000 9.400 ;
+ RECT 3.800 5.600 4.200 6.000 ;
+ RECT 7.200 5.600 7.600 6.000 ;
+ RECT 8.600 11.000 9.000 11.400 ;
+ RECT 8.600 5.400 9.000 5.800 ;
+ END
+END XOR2X1
+
+MACRO MUX2X1
+ CLASS CORE ;
+ FOREIGN MUX2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 9.600 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 6.800 8.600 7.600 10.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 7.800 2.800 9.400 ;
+ END
+ END B
+ PIN S
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 7.800 1.200 9.400 ;
+ END
+ END S
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.600 ;
+ RECT -0.400 -0.600 10.000 0.600 ;
+ RECT 7.200 -0.600 8.000 6.000 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 4.600 2.000 5.400 5.600 ;
+ RECT 5.600 6.600 7.600 7.400 ;
+ RECT 4.600 11.200 6.200 11.800 ;
+ RECT 5.600 5.000 6.200 11.800 ;
+ RECT 5.400 5.000 6.200 6.000 ;
+ RECT 4.600 11.200 5.400 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 11.200 2.800 20.600 ;
+ RECT -0.400 19.400 10.000 20.600 ;
+ RECT 7.200 10.800 8.000 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 0.400 2.000 1.200 4.000 ;
+ RECT 0.400 2.000 1.000 6.800 ;
+ RECT 0.400 6.200 4.600 6.800 ;
+ RECT 3.600 6.200 4.600 8.000 ;
+ RECT 3.600 7.200 5.000 8.000 ;
+ RECT 3.600 6.200 4.200 10.600 ;
+ RECT 0.400 10.000 4.200 10.600 ;
+ RECT 0.400 10.000 1.000 18.000 ;
+ RECT 0.400 14.000 1.200 18.000 ;
+ END
+END MUX2X1
+
+MACRO XNOR2X1
+ CLASS CORE ;
+ FOREIGN XNOR2X1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 11.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 3.600 5.400 4.400 6.200 ;
+ RECT 7.000 5.400 7.800 6.200 ;
+ RECT 3.600 5.400 7.800 6.000 ;
+ LAYER via1 ;
+ RECT 3.800 5.600 4.200 6.000 ;
+ RECT 7.200 5.600 7.600 6.000 ;
+ LAYER metal1 ;
+ RECT 0.400 6.600 2.000 7.400 ;
+ RECT 7.200 6.600 8.000 7.400 ;
+ RECT 7.200 5.400 7.800 7.400 ;
+ RECT 7.000 5.400 7.800 6.200 ;
+ RECT 3.600 5.400 5.200 6.200 ;
+ RECT 0.400 6.600 3.800 7.200 ;
+ RECT 3.200 5.600 3.800 7.200 ;
+ END
+ END A
+ PIN B
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 9.200 6.600 10.800 7.400 ;
+ END
+ END B
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.200 -0.600 3.000 4.600 ;
+ RECT -0.400 -0.600 11.600 0.600 ;
+ RECT 8.200 -0.600 9.200 4.600 ;
+ RECT 2.000 1.200 3.000 4.600 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 4.800 1.200 6.400 4.800 ;
+ RECT 6.200 8.600 7.600 9.400 ;
+ RECT 6.200 8.200 6.800 11.400 ;
+ RECT 4.800 10.800 6.400 18.800 ;
+ RECT 5.800 1.200 6.400 8.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 12.200 3.000 18.800 ;
+ RECT -0.400 19.400 11.600 20.600 ;
+ RECT 8.200 12.200 9.200 20.600 ;
+ RECT 2.200 12.200 3.000 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 1.800 5.200 2.600 6.000 ;
+ RECT 1.800 5.200 2.400 11.600 ;
+ RECT 1.800 10.800 2.600 11.600 ;
+ RECT 8.400 5.200 9.200 6.000 ;
+ RECT 4.400 6.800 9.200 7.400 ;
+ RECT 4.400 6.800 5.200 7.600 ;
+ RECT 8.600 5.200 9.200 11.600 ;
+ RECT 8.400 10.800 9.200 11.600 ;
+ LAYER metal1 ;
+ RECT 0.400 1.200 1.200 5.800 ;
+ RECT 0.400 5.200 2.600 5.800 ;
+ RECT 1.800 5.200 2.600 6.000 ;
+ RECT 4.400 6.800 5.200 7.600 ;
+ RECT 4.400 6.800 5.000 8.800 ;
+ RECT 2.400 8.200 5.000 8.800 ;
+ RECT 2.400 8.200 3.200 9.000 ;
+ RECT 4.600 9.400 5.400 10.200 ;
+ RECT 2.000 9.600 5.400 10.200 ;
+ RECT 0.400 10.800 2.600 11.400 ;
+ RECT 2.000 9.600 2.600 11.600 ;
+ RECT 1.800 10.800 2.600 11.600 ;
+ RECT 0.400 10.800 1.200 18.800 ;
+ RECT 8.400 10.800 10.800 11.400 ;
+ RECT 8.400 10.800 9.200 11.600 ;
+ RECT 10.000 10.800 10.800 18.800 ;
+ RECT 10.000 1.200 10.800 5.800 ;
+ RECT 8.400 5.200 10.800 5.800 ;
+ RECT 8.400 5.200 9.200 6.000 ;
+ LAYER via1 ;
+ RECT 2.000 11.000 2.400 11.400 ;
+ RECT 2.000 5.400 2.400 5.800 ;
+ RECT 4.600 7.000 5.000 7.400 ;
+ RECT 8.600 11.000 9.000 11.400 ;
+ RECT 8.600 5.400 9.000 5.800 ;
+ END
+END XNOR2X1
+
+MACRO LATCH
+ CLASS CORE ;
+ FOREIGN LATCH 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 11.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN Q
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 7.400 7.600 10.800 8.400 ;
+ RECT 10.000 1.200 10.800 18.800 ;
+ END
+ END Q
+ PIN CLK
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 1.200 6.600 2.800 7.400 ;
+ RECT 5.800 6.600 6.600 8.200 ;
+ RECT 1.200 6.600 6.600 7.200 ;
+ RECT 4.400 4.600 5.200 7.200 ;
+ END
+ END CLK
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.600 9.400 4.400 10.200 ;
+ RECT 3.600 9.400 4.400 11.400 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 -0.600 2.800 5.200 ;
+ RECT -0.400 -0.600 11.600 0.600 ;
+ RECT 8.400 -0.600 9.200 5.200 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 2.000 10.800 2.800 20.600 ;
+ RECT -0.400 19.400 11.600 20.600 ;
+ RECT 8.400 10.800 9.200 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 0.400 5.200 1.200 10.800 ;
+ RECT 5.200 3.200 6.000 14.800 ;
+ LAYER metal1 ;
+ RECT 0.400 9.800 1.200 18.800 ;
+ RECT 0.400 1.200 1.200 6.000 ;
+ RECT 0.400 8.000 5.000 8.600 ;
+ RECT 0.400 8.000 1.200 8.800 ;
+ RECT 4.200 8.000 5.000 8.800 ;
+ RECT 5.200 14.000 6.000 18.800 ;
+ RECT 5.000 14.800 6.200 18.800 ;
+ RECT 5.000 1.200 6.200 3.200 ;
+ RECT 5.200 1.200 6.000 4.000 ;
+ RECT 5.200 9.400 9.400 10.200 ;
+ LAYER via1 ;
+ RECT 0.600 10.200 1.000 10.600 ;
+ RECT 0.600 8.200 1.000 8.600 ;
+ RECT 0.600 5.400 1.000 5.800 ;
+ RECT 5.400 14.200 5.800 14.600 ;
+ RECT 5.400 9.600 5.800 10.000 ;
+ RECT 5.400 3.400 5.800 3.800 ;
+ END
+END LATCH
+
+MACRO DFFSR
+ CLASS CORE ;
+ FOREIGN DFFSR 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 35.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN Q
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 32.400 1.200 33.200 5.800 ;
+ RECT 32.600 5.000 33.400 11.000 ;
+ RECT 32.400 10.200 33.200 18.800 ;
+ END
+ END Q
+ PIN CLK
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 16.400 4.600 18.000 5.400 ;
+ END
+ END CLK
+ PIN R
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 1.800 8.800 2.600 9.600 ;
+ RECT 1.800 9.000 25.200 9.600 ;
+ RECT 24.400 8.400 25.200 9.600 ;
+ RECT 6.800 8.600 7.600 9.600 ;
+ END
+ END R
+ PIN S
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 10.200 4.400 11.400 ;
+ RECT 3.600 10.200 30.600 10.800 ;
+ RECT 29.800 10.000 30.600 10.800 ;
+ RECT 7.000 10.200 7.800 11.000 ;
+ END
+ END S
+ PIN D
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 13.200 5.800 14.000 7.400 ;
+ END
+ END D
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ RECT -0.400 -0.600 35.600 0.600 ;
+ RECT 34.000 -0.600 34.800 3.200 ;
+ RECT 27.600 -0.600 28.400 5.200 ;
+ RECT 16.400 -0.600 17.200 3.200 ;
+ RECT 13.200 -0.600 14.000 3.200 ;
+ END
+ END gnd
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 14.800 1.200 20.600 ;
+ RECT -0.400 19.400 35.600 20.600 ;
+ RECT 34.000 14.800 34.800 20.600 ;
+ RECT 30.800 14.800 31.600 20.600 ;
+ RECT 27.600 14.800 28.400 20.600 ;
+ RECT 24.400 14.800 25.200 20.600 ;
+ RECT 16.400 14.800 17.200 20.600 ;
+ RECT 13.200 14.800 14.000 20.600 ;
+ RECT 6.800 14.800 7.600 20.600 ;
+ RECT 3.600 14.800 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal2 ;
+ RECT 8.400 3.200 9.200 16.800 ;
+ RECT 10.000 3.200 10.800 16.800 ;
+ RECT 11.600 3.200 12.400 14.800 ;
+ RECT 14.800 3.200 15.600 14.800 ;
+ RECT 18.000 3.200 18.800 14.800 ;
+ RECT 19.600 3.200 20.400 16.800 ;
+ RECT 21.200 3.200 22.000 16.800 ;
+ RECT 22.800 3.200 23.600 16.800 ;
+ LAYER metal1 ;
+ RECT 6.800 1.200 7.600 5.200 ;
+ RECT 5.000 4.400 7.600 5.200 ;
+ RECT 5.000 4.400 5.800 6.600 ;
+ RECT 2.800 5.800 5.800 6.600 ;
+ RECT 8.400 16.000 9.200 18.800 ;
+ RECT 0.400 7.200 9.200 8.000 ;
+ RECT 0.400 1.200 1.200 14.200 ;
+ RECT 0.400 13.600 2.400 14.200 ;
+ RECT 1.800 13.600 2.400 15.400 ;
+ RECT 2.000 14.800 2.800 18.800 ;
+ RECT 8.400 1.200 9.200 4.000 ;
+ RECT 10.000 16.000 10.800 18.800 ;
+ RECT 5.400 11.600 10.800 12.200 ;
+ RECT 10.000 11.400 10.800 12.200 ;
+ RECT 4.600 12.000 6.000 12.800 ;
+ RECT 10.000 1.200 10.800 4.000 ;
+ RECT 11.600 14.000 12.400 18.800 ;
+ RECT 11.600 1.200 12.400 4.000 ;
+ RECT 14.800 14.000 15.600 18.800 ;
+ RECT 13.000 12.600 15.600 13.400 ;
+ RECT 11.000 4.600 15.600 5.200 ;
+ RECT 14.800 4.600 15.600 5.400 ;
+ RECT 11.000 4.600 11.800 8.400 ;
+ RECT 9.800 7.600 11.800 8.400 ;
+ RECT 14.800 1.200 15.600 4.000 ;
+ RECT 18.000 12.600 18.800 18.800 ;
+ RECT 15.600 7.600 18.800 8.400 ;
+ RECT 18.000 1.200 18.800 4.000 ;
+ RECT 14.800 6.000 15.600 6.800 ;
+ RECT 14.800 6.200 19.200 6.800 ;
+ RECT 18.400 6.200 19.200 7.000 ;
+ RECT 19.600 16.000 20.400 18.800 ;
+ RECT 11.800 11.400 20.400 12.000 ;
+ RECT 19.600 11.400 20.400 12.200 ;
+ RECT 2.000 12.200 3.600 13.000 ;
+ RECT 3.000 12.200 3.600 14.200 ;
+ RECT 6.600 12.800 12.400 13.400 ;
+ RECT 11.800 11.400 12.400 13.400 ;
+ RECT 3.000 13.400 7.200 14.200 ;
+ RECT 5.200 13.400 6.000 18.800 ;
+ RECT 19.600 1.200 20.400 4.000 ;
+ RECT 21.200 16.000 22.000 18.800 ;
+ RECT 21.000 4.600 22.000 5.400 ;
+ RECT 21.200 4.600 22.000 8.200 ;
+ RECT 21.200 1.200 22.000 4.000 ;
+ RECT 22.800 16.000 23.600 18.800 ;
+ RECT 22.800 1.200 23.600 4.000 ;
+ RECT 21.200 13.400 25.400 14.200 ;
+ RECT 26.000 12.600 28.600 13.400 ;
+ RECT 26.000 12.600 26.800 18.800 ;
+ RECT 24.400 1.200 25.200 5.200 ;
+ RECT 24.400 4.400 26.800 5.200 ;
+ RECT 26.000 4.400 26.800 6.400 ;
+ RECT 26.000 5.800 27.800 6.400 ;
+ RECT 27.000 5.800 27.800 8.400 ;
+ RECT 27.000 7.600 30.800 8.400 ;
+ RECT 30.800 1.200 31.600 7.000 ;
+ RECT 31.400 6.400 32.000 9.600 ;
+ RECT 22.800 11.400 31.800 12.000 ;
+ RECT 22.800 11.400 23.600 12.200 ;
+ RECT 31.200 9.000 31.800 14.200 ;
+ RECT 29.200 13.600 31.800 14.200 ;
+ RECT 29.200 13.600 30.000 18.800 ;
+ LAYER via1 ;
+ RECT 8.600 16.200 9.000 16.600 ;
+ RECT 8.600 7.400 9.000 7.800 ;
+ RECT 8.600 3.400 9.000 3.800 ;
+ RECT 10.200 16.200 10.600 16.600 ;
+ RECT 10.200 11.600 10.600 12.000 ;
+ RECT 10.200 3.400 10.600 3.800 ;
+ RECT 11.800 14.200 12.200 14.600 ;
+ RECT 11.800 3.400 12.200 3.800 ;
+ RECT 15.000 14.200 15.400 14.600 ;
+ RECT 15.000 12.800 15.400 13.200 ;
+ RECT 15.000 6.200 15.400 6.600 ;
+ RECT 15.000 3.400 15.400 3.800 ;
+ RECT 18.200 14.200 18.600 14.600 ;
+ RECT 18.200 7.800 18.600 8.200 ;
+ RECT 18.200 3.400 18.600 3.800 ;
+ RECT 19.800 16.200 20.200 16.600 ;
+ RECT 19.800 11.600 20.200 12.000 ;
+ RECT 19.800 3.400 20.200 3.800 ;
+ RECT 21.400 16.200 21.800 16.600 ;
+ RECT 21.400 13.600 21.800 14.000 ;
+ RECT 21.400 3.400 21.800 3.800 ;
+ RECT 23.000 16.200 23.400 16.600 ;
+ RECT 23.000 11.600 23.400 12.000 ;
+ RECT 23.000 3.400 23.400 3.800 ;
+ END
+END DFFSR
+
+MACRO CLKBUF1
+ CLASS CORE ;
+ FOREIGN CLKBUF1 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 14.400 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 8.000 ;
+ RECT 0.400 7.200 2.200 8.000 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 14.800 0.600 ;
+ RECT 13.200 -0.600 14.000 5.200 ;
+ RECT 10.000 -0.600 10.800 5.200 ;
+ RECT 6.800 -0.600 7.600 5.200 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 11.600 1.200 12.400 6.600 ;
+ RECT 11.600 9.400 14.000 10.200 ;
+ RECT 13.200 5.800 14.000 10.200 ;
+ RECT 11.600 5.800 14.000 6.600 ;
+ RECT 11.600 9.400 12.400 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 14.800 20.600 ;
+ RECT 13.200 10.800 14.000 20.600 ;
+ RECT 10.000 10.800 10.800 20.600 ;
+ RECT 6.800 10.800 7.600 20.600 ;
+ RECT 3.600 10.800 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 6.600 ;
+ RECT 2.000 5.800 3.800 6.600 ;
+ RECT 3.000 7.200 5.600 8.000 ;
+ RECT 3.000 5.800 3.800 10.200 ;
+ RECT 2.000 9.400 3.800 10.200 ;
+ RECT 2.000 9.400 2.800 18.800 ;
+ RECT 5.200 1.200 6.000 6.600 ;
+ RECT 5.200 5.800 7.400 6.600 ;
+ RECT 6.600 7.200 9.000 8.000 ;
+ RECT 6.600 5.800 7.400 10.200 ;
+ RECT 5.200 9.400 7.400 10.200 ;
+ RECT 5.200 9.400 6.000 18.800 ;
+ RECT 8.400 1.200 9.200 6.600 ;
+ RECT 8.400 5.800 10.600 6.600 ;
+ RECT 9.800 7.200 12.400 8.000 ;
+ RECT 9.800 5.800 10.600 10.200 ;
+ RECT 8.400 9.400 10.600 10.200 ;
+ RECT 8.400 9.400 9.200 18.800 ;
+ END
+END CLKBUF1
+
+MACRO CLKBUF2
+ CLASS CORE ;
+ FOREIGN CLKBUF2 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 20.800 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 8.000 ;
+ RECT 0.400 7.200 2.200 8.000 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 21.200 0.600 ;
+ RECT 19.600 -0.600 20.400 5.200 ;
+ RECT 16.400 -0.600 17.200 5.200 ;
+ RECT 13.200 -0.600 14.000 5.200 ;
+ RECT 10.000 -0.600 10.800 5.200 ;
+ RECT 6.800 -0.600 7.600 5.200 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 18.000 1.200 18.800 6.600 ;
+ RECT 18.000 9.400 20.400 10.200 ;
+ RECT 19.600 5.800 20.400 10.200 ;
+ RECT 18.000 5.800 20.400 6.600 ;
+ RECT 18.000 9.400 18.800 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 21.200 20.600 ;
+ RECT 19.600 10.800 20.400 20.600 ;
+ RECT 16.400 10.800 17.200 20.600 ;
+ RECT 13.200 10.800 14.000 20.600 ;
+ RECT 10.000 10.800 10.800 20.600 ;
+ RECT 6.800 10.800 7.600 20.600 ;
+ RECT 3.600 10.800 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 6.600 ;
+ RECT 2.000 5.800 3.800 6.600 ;
+ RECT 3.000 7.200 5.600 8.000 ;
+ RECT 3.000 5.800 3.800 10.200 ;
+ RECT 2.000 9.400 3.800 10.200 ;
+ RECT 2.000 9.400 2.800 18.800 ;
+ RECT 5.200 1.200 6.000 6.600 ;
+ RECT 5.200 5.800 7.400 6.600 ;
+ RECT 6.600 7.200 9.000 8.000 ;
+ RECT 6.600 5.800 7.400 10.200 ;
+ RECT 5.200 9.400 7.400 10.200 ;
+ RECT 5.200 9.400 6.000 18.800 ;
+ RECT 8.400 1.200 9.200 6.600 ;
+ RECT 8.400 5.800 10.600 6.600 ;
+ RECT 9.800 7.200 12.400 8.000 ;
+ RECT 9.800 5.800 10.600 10.200 ;
+ RECT 8.400 9.400 10.600 10.200 ;
+ RECT 8.400 9.400 9.200 18.800 ;
+ RECT 11.600 1.200 12.400 6.600 ;
+ RECT 11.600 5.800 14.000 6.600 ;
+ RECT 13.200 7.200 15.000 8.000 ;
+ RECT 13.200 5.800 14.000 10.200 ;
+ RECT 11.600 9.400 14.000 10.200 ;
+ RECT 11.600 9.400 12.400 18.800 ;
+ RECT 14.800 1.200 15.600 6.600 ;
+ RECT 14.800 5.800 16.600 6.600 ;
+ RECT 15.800 7.200 18.400 8.000 ;
+ RECT 15.800 5.800 16.600 10.200 ;
+ RECT 14.800 9.400 16.600 10.200 ;
+ RECT 14.800 9.400 15.600 18.800 ;
+ END
+END CLKBUF2
+
+MACRO CLKBUF3
+ CLASS CORE ;
+ FOREIGN CLKBUF3 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 27.200 BY 20.000 ;
+ SYMMETRY X Y ;
+ SITE core ;
+ PIN A
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 6.600 1.200 8.000 ;
+ RECT 0.400 7.200 2.200 8.000 ;
+ END
+ END A
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 -0.600 1.200 5.200 ;
+ RECT -0.400 -0.600 27.600 0.600 ;
+ RECT 26.000 -0.600 26.800 5.200 ;
+ RECT 22.800 -0.600 23.600 5.200 ;
+ RECT 19.600 -0.600 20.400 5.200 ;
+ RECT 16.400 -0.600 17.200 5.200 ;
+ RECT 13.200 -0.600 14.000 5.200 ;
+ RECT 10.000 -0.600 10.800 5.200 ;
+ RECT 6.800 -0.600 7.600 5.200 ;
+ RECT 3.600 -0.600 4.400 5.200 ;
+ END
+ END gnd
+ PIN Y
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal1 ;
+ RECT 24.400 1.200 25.200 6.600 ;
+ RECT 24.400 9.400 26.800 10.200 ;
+ RECT 26.000 5.800 26.800 10.200 ;
+ RECT 24.400 5.800 26.800 6.600 ;
+ RECT 24.400 9.400 25.200 18.800 ;
+ END
+ END Y
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ SHAPE ABUTMENT ;
+ PORT
+ LAYER metal1 ;
+ RECT 0.400 10.800 1.200 20.600 ;
+ RECT -0.400 19.400 27.600 20.600 ;
+ RECT 26.000 10.800 26.800 20.600 ;
+ RECT 22.800 10.800 23.600 20.600 ;
+ RECT 19.600 10.800 20.400 20.600 ;
+ RECT 16.400 10.800 17.200 20.600 ;
+ RECT 13.200 10.800 14.000 20.600 ;
+ RECT 10.000 10.800 10.800 20.600 ;
+ RECT 6.800 10.800 7.600 20.600 ;
+ RECT 3.600 10.800 4.400 20.600 ;
+ END
+ END vdd
+ OBS
+ LAYER metal1 ;
+ RECT 2.000 1.200 2.800 6.600 ;
+ RECT 2.000 5.800 3.800 6.600 ;
+ RECT 3.000 7.200 5.600 8.000 ;
+ RECT 3.000 5.800 3.800 10.200 ;
+ RECT 2.000 9.400 3.800 10.200 ;
+ RECT 2.000 9.400 2.800 18.800 ;
+ RECT 5.200 1.200 6.000 6.600 ;
+ RECT 5.200 5.800 7.400 6.600 ;
+ RECT 6.600 7.200 9.000 8.000 ;
+ RECT 6.600 5.800 7.400 10.200 ;
+ RECT 5.200 9.400 7.400 10.200 ;
+ RECT 5.200 9.400 6.000 18.800 ;
+ RECT 8.400 1.200 9.200 6.600 ;
+ RECT 8.400 5.800 10.600 6.600 ;
+ RECT 9.800 7.200 12.400 8.000 ;
+ RECT 9.800 5.800 10.600 10.200 ;
+ RECT 8.400 9.400 10.600 10.200 ;
+ RECT 8.400 9.400 9.200 18.800 ;
+ RECT 11.600 1.200 12.400 6.600 ;
+ RECT 11.600 5.800 14.000 6.600 ;
+ RECT 13.200 7.200 15.000 8.000 ;
+ RECT 13.200 5.800 14.000 10.200 ;
+ RECT 11.600 9.400 14.000 10.200 ;
+ RECT 11.600 9.400 12.400 18.800 ;
+ RECT 14.800 1.200 15.600 6.600 ;
+ RECT 14.800 5.800 16.600 6.600 ;
+ RECT 15.800 7.200 18.400 8.000 ;
+ RECT 15.800 5.800 16.600 10.200 ;
+ RECT 14.800 9.400 16.600 10.200 ;
+ RECT 14.800 9.400 15.600 18.800 ;
+ RECT 18.000 1.200 18.800 6.600 ;
+ RECT 18.000 5.800 20.200 6.600 ;
+ RECT 19.400 7.200 21.800 8.000 ;
+ RECT 19.400 5.800 20.200 10.200 ;
+ RECT 18.000 9.400 20.200 10.200 ;
+ RECT 18.000 9.400 18.800 18.800 ;
+ RECT 21.200 1.200 22.000 6.600 ;
+ RECT 21.200 5.800 23.400 6.600 ;
+ RECT 22.600 7.200 25.200 8.000 ;
+ RECT 22.600 5.800 23.400 10.200 ;
+ RECT 21.200 9.400 23.400 10.200 ;
+ RECT 21.200 9.400 22.000 18.800 ;
+ END
+END CLKBUF3
+
+MACRO PADFC
+ CLASS ENDCAP TOPLEFT ;
+ FOREIGN PADFC 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 300.000 BY 300.000 ;
+ SYMMETRY X Y R90 ;
+ SITE corner ;
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 299.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 299.400 299.400 ;
+ LAYER metal2 ;
+ RECT 98.000 0.000 170.400 299.400 ;
+ RECT 174.800 0.000 195.800 299.400 ;
+ RECT 202.200 0.000 223.200 299.400 ;
+ RECT 227.600 0.000 300.000 72.400 ;
+ RECT 0.600 76.800 300.000 97.800 ;
+ RECT 0.600 104.000 300.000 125.200 ;
+ RECT 0.600 129.600 300.000 202.000 ;
+ RECT 0.600 0.600 299.400 299.400 ;
+ LAYER metal1 ;
+ RECT 98.000 0.000 195.800 299.400 ;
+ RECT 202.200 0.000 300.000 97.800 ;
+ RECT 0.600 104.000 300.000 202.000 ;
+ RECT 0.600 0.600 299.400 299.400 ;
+ END
+END PADFC
+
+MACRO PADGND
+ CLASS PAD ;
+ FOREIGN PADGND 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ PIN YPAD
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal4 ;
+ RECT 37.400 254.800 51.200 269.800 ;
+ END
+ END YPAD
+ PIN gnd
+ DIRECTION INOUT ;
+ USE GROUND ;
+ PORT
+ CLASS CORE ;
+ LAYER metal1 ;
+ RECT 34.800 0.000 54.800 0.800 ;
+ END
+ END gnd
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 253.000 ;
+ RECT 0.600 271.600 89.400 299.000 ;
+ RECT 0.600 0.600 35.600 299.400 ;
+ RECT 53.000 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 0.000 0.000 90.000 72.400 ;
+ RECT 0.600 76.600 89.600 97.800 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ RECT 6.000 0.000 84.000 300.000 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 34.200 97.800 ;
+ RECT 55.600 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 1.800 89.400 299.400 ;
+ RECT 6.000 1.800 84.000 300.000 ;
+ END
+END PADGND
+
+MACRO PADVDD
+ CLASS PAD ;
+ FOREIGN PADVDD 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ PIN YPAD
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal4 ;
+ RECT 42.200 266.000 44.400 268.400 ;
+ END
+ END YPAD
+ PIN vdd
+ DIRECTION INOUT ;
+ USE POWER ;
+ PORT
+ CLASS CORE ;
+ LAYER metal1 ;
+ RECT 35.400 0.000 54.600 0.800 ;
+ END
+ END vdd
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 264.200 ;
+ RECT 0.600 270.200 89.400 299.000 ;
+ RECT 0.600 0.600 40.400 299.400 ;
+ RECT 46.200 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 0.000 0.000 34.400 72.400 ;
+ RECT 35.400 0.000 54.600 300.000 ;
+ RECT 55.600 0.000 90.000 72.400 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 1.800 89.400 299.400 ;
+ RECT 6.000 1.800 84.000 300.000 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 34.400 97.800 ;
+ RECT 55.600 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 1.800 89.400 299.400 ;
+ RECT 6.000 1.800 84.000 300.000 ;
+ END
+END PADVDD
+
+MACRO PADINC
+ CLASS PAD ;
+ FOREIGN PADINC 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ PIN YPAD
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal4 ;
+ RECT 42.200 266.000 44.400 268.400 ;
+ END
+ END YPAD
+ PIN DI
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 82.200 -0.400 83.000 0.400 ;
+ RECT 81.600 0.000 83.400 0.400 ;
+ END
+ END DI
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 264.200 ;
+ RECT 0.600 270.200 89.400 299.000 ;
+ RECT 0.600 0.600 40.400 299.400 ;
+ RECT 46.200 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 80.600 299.400 ;
+ RECT 84.400 0.600 89.400 299.400 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 0.000 0.000 34.400 72.400 ;
+ RECT 35.400 0.000 54.600 300.000 ;
+ RECT 55.600 0.000 78.200 300.000 ;
+ RECT 79.000 0.000 80.800 300.000 ;
+ RECT 0.000 0.600 80.800 72.400 ;
+ RECT 84.200 0.000 90.000 72.400 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ RECT 6.000 1.400 84.000 300.000 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ RECT 6.000 0.000 84.000 300.000 ;
+ END
+END PADINC
+
+MACRO PADINOUT
+ CLASS PAD ;
+ FOREIGN PADINOUT 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ PIN YPAD
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal4 ;
+ RECT 42.200 266.000 44.400 268.400 ;
+ END
+ END YPAD
+ PIN DO
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 8.200 -0.400 9.000 0.400 ;
+ RECT 7.600 0.000 9.400 0.400 ;
+ END
+ END DO
+ PIN DI
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 82.200 -0.400 83.000 0.400 ;
+ RECT 81.600 0.000 83.400 0.400 ;
+ END
+ END DI
+ PIN OEN
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 2.600 -0.400 3.400 0.400 ;
+ RECT 2.200 0.000 4.000 0.400 ;
+ END
+ END OEN
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 264.200 ;
+ RECT 0.600 270.200 89.400 299.000 ;
+ RECT 0.600 0.600 40.400 299.400 ;
+ RECT 46.200 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 1.200 299.400 ;
+ RECT 5.000 0.600 6.600 299.400 ;
+ RECT 10.400 0.600 80.600 299.400 ;
+ RECT 84.400 0.600 89.400 299.400 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 10.200 0.000 34.400 300.000 ;
+ RECT 35.400 0.000 54.600 300.000 ;
+ RECT 55.600 0.000 78.200 300.000 ;
+ RECT 0.000 0.000 1.400 72.400 ;
+ RECT 4.800 0.000 6.800 299.400 ;
+ RECT 79.000 0.000 80.800 300.000 ;
+ RECT 10.200 0.600 80.800 300.000 ;
+ RECT 84.200 0.000 90.000 72.400 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ RECT 6.000 1.400 84.000 300.000 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ RECT 6.000 0.000 84.000 300.000 ;
+ END
+END PADINOUT
+
+MACRO PADNC
+ CLASS PAD ;
+ FOREIGN PADNC 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 0.000 0.000 90.000 72.400 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ END
+END PADNC
+
+MACRO PADOUT
+ CLASS PAD ;
+ FOREIGN PADOUT 0.000 0.000 ;
+ ORIGIN 0.000 0.000 ;
+ SIZE 90.000 BY 300.000 ;
+ SYMMETRY R90 ;
+ SITE IO ;
+ PIN YPAD
+ DIRECTION OUTPUT ;
+ PORT
+ LAYER metal4 ;
+ RECT 42.200 266.000 44.400 268.400 ;
+ END
+ END YPAD
+ PIN DO
+ DIRECTION INPUT ;
+ PORT
+ LAYER metal2 ;
+ RECT 8.200 -0.400 9.000 0.400 ;
+ RECT 7.600 0.000 9.400 0.400 ;
+ END
+ END DO
+ OBS
+ LAYER metal4 ;
+ RECT 0.600 0.600 89.400 264.200 ;
+ RECT 0.600 270.200 89.400 299.000 ;
+ RECT 0.600 0.600 40.400 299.400 ;
+ RECT 46.200 0.600 89.400 299.400 ;
+ LAYER metal3 ;
+ RECT 0.600 0.600 6.600 299.400 ;
+ RECT 10.400 0.600 89.400 299.400 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ LAYER metal2 ;
+ RECT 0.000 0.000 1.400 72.400 ;
+ RECT 2.200 0.000 4.000 299.400 ;
+ RECT 4.800 0.000 6.800 299.400 ;
+ RECT 10.200 0.000 34.400 300.000 ;
+ RECT 35.400 0.000 54.600 300.000 ;
+ RECT 55.600 0.000 78.200 300.000 ;
+ RECT 79.000 0.000 80.800 300.000 ;
+ RECT 81.600 0.000 83.400 300.000 ;
+ RECT 0.000 0.600 6.800 72.400 ;
+ RECT 10.200 0.600 90.000 72.400 ;
+ RECT 84.200 0.000 90.000 72.400 ;
+ RECT 0.000 76.800 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 125.200 ;
+ RECT 0.000 129.600 90.000 202.000 ;
+ RECT 0.600 1.400 89.400 299.400 ;
+ RECT 6.000 1.400 84.000 300.000 ;
+ LAYER metal1 ;
+ RECT 0.000 0.000 90.000 97.800 ;
+ RECT 0.000 104.200 90.000 202.000 ;
+ RECT 0.600 0.000 89.400 299.400 ;
+ RECT 6.000 0.000 84.000 300.000 ;
+ END
+END PADOUT
+
+END LIBRARY
diff --git a/tech/osu035_redm4/osu035_stdcells.gds2 b/tech/osu035_redm4/osu035_stdcells.gds2
new file mode 120000
index 0000000..d81a7c3
--- /dev/null
+++ b/tech/osu035_redm4/osu035_stdcells.gds2
@@ -0,0 +1 @@
+../osu035/osu035_stdcells.gds2 \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_stdcells.lib b/tech/osu035_redm4/osu035_stdcells.lib
new file mode 120000
index 0000000..13a16bd
--- /dev/null
+++ b/tech/osu035_redm4/osu035_stdcells.lib
@@ -0,0 +1 @@
+../osu035/osu035_stdcells.lib \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_stdcells.sp b/tech/osu035_redm4/osu035_stdcells.sp
new file mode 120000
index 0000000..e17fb0f
--- /dev/null
+++ b/tech/osu035_redm4/osu035_stdcells.sp
@@ -0,0 +1 @@
+../osu035/osu035_stdcells.sp \ No newline at end of file
diff --git a/tech/osu035_redm4/osu035_stdcells.v b/tech/osu035_redm4/osu035_stdcells.v
new file mode 120000
index 0000000..abb784d
--- /dev/null
+++ b/tech/osu035_redm4/osu035_stdcells.v
@@ -0,0 +1 @@
+../osu035/osu035_stdcells.v \ No newline at end of file