diff options
84 files changed, 23293 insertions, 1879 deletions
@@ -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 @@ -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 @@ -1 +1 @@ -1.3.12 +1.4.62 @@ -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", ©special); + 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++) { @@ -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)); } @@ -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 |