diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2017-07-13 22:25:58 +0200 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2017-07-13 22:25:58 +0200 |
commit | 6bfa88a0ac7a4f8d69b344edd1795404e9048d20 (patch) | |
tree | 6ffa0b15851a01e9ab4ea8abf83001e769c773ef /scripts | |
parent | 548aa8a30317aade2d08d55499cf8860513bd081 (diff) |
New upstream version 1.1.58
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/Makefile | 109 | ||||
-rw-r--r-- | scripts/Makefile.in | 10 | ||||
-rwxr-xr-x | scripts/addspacers.tcl.in | 1414 | ||||
-rwxr-xr-x | scripts/placement.sh | 70 | ||||
-rw-r--r-- | scripts/qflow.sh.in | 17 | ||||
-rwxr-xr-x | scripts/router.sh | 4 | ||||
-rwxr-xr-x | scripts/spi2xspice.py | 762 | ||||
-rwxr-xr-x | scripts/synthesize.sh | 86 |
8 files changed, 2057 insertions, 415 deletions
diff --git a/scripts/Makefile b/scripts/Makefile deleted file mode 100644 index a9f600e..0000000 --- a/scripts/Makefile +++ /dev/null @@ -1,109 +0,0 @@ -# -# qflow project scripts makefile -# - -# Main compiler arguments -CFLAGS = -g -O2 -DEFS = -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DSTDC_HEADERS=1 -DHAVE_SETENV=1 -DHAVE_PUTENV=1 -DTCLSH_PATH=\"/bin/tclsh\" -DQFLOW_MAGIC_PATH=\"/usr/local/bin/magic\" -DQFLOW_QROUTER_PATH=\"/usr/local/bin/qrouter\" -DQFLOW_GRAYWOLF_PATH=\"/usr/local/bin/graywolf\" -DQFLOW_YOSYS_PATH=\"/usr/local/bin/yosys\" -LIBS = -LDFLAGS = -INSTALL = /bin/install -c - -VERSION = 1.1 -REVISION = 33 - -QFLOW_LIB_DIR = /usr/local/share/qflow -QFLOW_BIN_DIR = /usr/local/bin -TCLSH_PATH = /bin/tclsh - -DEFAULTPARSER = @QFLOW_DEFAULT_PARSER@ - -TCL_SCRIPTS = blif2cel.tcl place2def.tcl place2lef2.tcl -TCL_SCRIPTS += place2net2.tcl ypostproc.tcl ybuffer.tcl -TCL_SCRIPTS += decongest.tcl addspacers.tcl getfillcell.tcl -TCL_SCRIPTS += blifanno.tcl powerbus.tcl -SHELL_SCRIPTS = synthesize.sh placement.sh router.sh vesta.sh -SHELL_SCRIPTS += qflow.sh checkdirs.sh -SHELL_SCRIPTS += cleanup.sh display.sh createGDS.sh -MAIN_SCRIPT = qflow - -SCRIPTINSTALL = ${QFLOW_LIB_DIR}/scripts -TECHINSTALL = ${QFLOW_LIB_DIR}/tech -QFLOWEXECPATH = ${QFLOW_LIB_DIR}/bin -EXECINSTALL = ${QFLOW_BIN_DIR} - -all: $(MAIN_SCRIPT).in qflow.sh - $(MAKE) launcher - -launcher: $(MAIN_SCRIPT).in - sed -e '/QFLOW_SCRIPT_DIR/s#QFLOW_SCRIPT_DIR#$(SCRIPTINSTALL)#' \ - $(MAIN_SCRIPT).in > $(MAIN_SCRIPT) - -checkdirs.sh: checkdirs.sh.in - sed -e '/SUBST_TECH_DIR/s#SUBST_TECH_DIR#$(TECHINSTALL)#' \ - -e '/SUBST_SCRIPT_DIR/s#SUBST_SCRIPT_DIR#$(SCRIPTINSTALL)#' \ - -e '/SUBST_BIN_DIR/s#SUBST_BIN_DIR#$(QFLOWEXECPATH)#' \ - checkdirs.sh.in > checkdirs.sh - -qflow.sh: qflow.sh.in - sed -e '/QFLOW_SCRIPT_DIR/s#QFLOW_SCRIPT_DIR#$(SCRIPTINSTALL)#' \ - -e '/QFLOW_DEFAULT_PARSER/s#QFLOW_DEFAULT_PARSER#$(DEFAULTPARSER)#' \ - -e '/QFLOW_REVISION/s#QFLOW_REVISION#$(REVISION)#' \ - -e '/QFLOW_VERSION/s#QFLOW_VERSION#$(VERSION)#' \ - qflow.sh.in > qflow.sh - -blif2cel.tcl: blif2cel.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' blif2cel.tcl.in > $@ - -place2def.tcl: place2def.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' place2def.tcl.in > $@ - -place2lef2.tcl: place2def2.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' place2lef2.tcl.in > $@ - -place2net2.tcl: place2net2.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' place2net2.tcl.in > $@ - -ypostproc.tcl: ypostproc.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' ypostproc.tcl.in > $@ - -ybuffer.tcl: ybuffer.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' ybuffer.tcl.in > $@ - -decongest.tcl: decongest.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' decongest.tcl.in > $@ - -powerbus.tcl: powerbus.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' powerbus.tcl.in > $@ - -addspacers.tcl: addspacers.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' addspacers.tcl.in > $@ - -getfillcell.tcl: getfillcell.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' getfillcell.tcl.in > $@ - -blifanno.tcl: blifanno.tcl.in - sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' blifanno.tcl.in > $@ - -install: $(TCL_SCRIPTS) $(SHELL_SCRIPTS) $(MAIN_SCRIPT) - @echo "Installing qflow scripts" - $(INSTALL) -d $(DESTDIR)${SCRIPTINSTALL} - for target in $(TCL_SCRIPTS); do \ - $(INSTALL) $$target $(DESTDIR)${SCRIPTINSTALL} ;\ - done - for target in $(SHELL_SCRIPTS); do \ - $(INSTALL) $$target $(DESTDIR)${SCRIPTINSTALL} ;\ - done - @echo "Installing qflow executable" - $(INSTALL) -d $(DESTDIR)${EXECINSTALL} - $(INSTALL) ${MAIN_SCRIPT} $(DESTDIR)${EXECINSTALL} - -clean: - $(RM) $(MAIN_SCRIPT) - $(RM) checkdirs.sh - $(RM) qflow.sh - $(RM) $(TCL_SCRIPTS) - -uninstall: - $(RM) -rf ${SCRIPTINSTALL} - $(RM) ${EXECINSTALL}/${MAIN_SCRIPT} diff --git a/scripts/Makefile.in b/scripts/Makefile.in index 76634c8..2519680 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -22,6 +22,7 @@ TCL_SCRIPTS = blif2cel.tcl place2def.tcl place2lef2.tcl TCL_SCRIPTS += place2net2.tcl ypostproc.tcl ybuffer.tcl TCL_SCRIPTS += decongest.tcl addspacers.tcl getfillcell.tcl TCL_SCRIPTS += blifanno.tcl powerbus.tcl +PYTHON_SCRIPTS = spi2xspice.py SHELL_SCRIPTS = synthesize.sh placement.sh router.sh vesta.sh SHELL_SCRIPTS += qflow.sh checkdirs.sh SHELL_SCRIPTS += cleanup.sh display.sh createGDS.sh @@ -85,15 +86,20 @@ getfillcell.tcl: getfillcell.tcl.in blifanno.tcl: blifanno.tcl.in sed -e 's#TCLSH_PATH#$(TCLSH_PATH)#' blifanno.tcl.in > $@ -install: $(TCL_SCRIPTS) $(SHELL_SCRIPTS) $(MAIN_SCRIPT) - @echo "Installing qflow scripts" +install: $(TCL_SCRIPTS) $(PYTHON_SCRIPTS) $(SHELL_SCRIPTS) $(MAIN_SCRIPT) + @echo "Installing qflow TCL scripts" $(INSTALL) -d $(DESTDIR)${SCRIPTINSTALL} for target in $(TCL_SCRIPTS); do \ $(INSTALL) $$target $(DESTDIR)${SCRIPTINSTALL} ;\ done + @echo "Installing qflow shell scripts" for target in $(SHELL_SCRIPTS); do \ $(INSTALL) $$target $(DESTDIR)${SCRIPTINSTALL} ;\ done + @echo "Installing qflow python scripts" + for target in $(PYTHON_SCRIPTS); do \ + $(INSTALL) $$target $(DESTDIR)${SCRIPTINSTALL} ;\ + done @echo "Installing qflow executable" $(INSTALL) -d $(DESTDIR)${EXECINSTALL} $(INSTALL) ${MAIN_SCRIPT} $(DESTDIR)${EXECINSTALL} diff --git a/scripts/addspacers.tcl.in b/scripts/addspacers.tcl.in index 9d535ed..02dab9c 100755 --- a/scripts/addspacers.tcl.in +++ b/scripts/addspacers.tcl.in @@ -13,12 +13,22 @@ # Modify the COMPONENTS section to add the spacer cells, and write out # the annotated DEF file. # +# Updated 6/7/2017 to add option to split layout at intervals and +# insert a stripe of minimum size for the addition of a power bus. +# This is an ad hoc solution that does not consider how the overall +# timing is affected by stretching routes that have to cross this +# stripe. Use of "-nostretch" will place the stripes over the existing +# layout without making extra space with fill. This is generally +# preferred unless it makes the cell unroutable. #--------------------------------------------------------------------------- + namespace path {::tcl::mathop ::tcl::mathfunc} if {$argc < 3} { - puts stdout "Usage: addspacers <project_name> <lef_file> <fill_cell>" - exit 0 + puts stdout "Usage: addspacers [<options>] <project_name> <lef_file> <fill_cell>" + puts stdout "Options:" + puts stdout " -stripe <width> <pitch> <pattern> [-nostretch] [-techlef <tech.lef>]" + exit 0 } puts stdout "Running addspacers.tcl" @@ -26,27 +36,254 @@ puts stdout "Running addspacers.tcl" # NOTE: There is no scaling. GrayWolf values are in centimicrons, # as are DEF values (UNITS DISTANCE MICRONS 100) -set topname [file rootname [lindex $argv 0]] -set lefname [lindex $argv 1] -set fillcell [lindex $argv 2] +set argidx 0 +set dostripes false ;# no power bus striping by default +set stripewidth 0 +set stripepitch 0 +set stripeoffset 0 +set numstripes 0 +set dostretch true +set techlef "" +set pitchx 0 +set layers {} +set vias {} + +while true { + set arg0 [lindex $argv $argidx] + if {$arg0 == "-stripe"} { + incr argidx + set dostripes true + set stripewidth [lindex $argv $argidx] + set stripewidthreq $stripewidth + incr argidx + set stripepitch [lindex $argv $argidx] + incr argidx + set stripepattern [lindex $argv $argidx] + } elseif {$arg0 == "-stripewidth"} { + incr argidx + set dostripes true + set stripewidthreq $stripewidth + set stripewidth [lindex $argv $argidx] + } elseif {$arg0 == "-stripepitch"} { + incr argidx + set dostripes true + set stripepitch [lindex $argv $argidx] + } elseif {$arg0 == "-stripepattern"} { + incr argidx + set dostripes true + set stripepattern [lindex $argv $argidx] + } elseif {$arg0 == "-techlef"} { + incr argidx + set techlef [lindex $argv $argidx] + } elseif {$arg0 == "-nostretch"} { + set dostretch false + } else { + break + } + incr argidx +} + +set topname [file rootname [lindex $argv $argidx]] +incr argidx +set lefname [lindex $argv $argidx] +incr argidx +set fillcell [lindex $argv $argidx] set defname ${topname}.def set defoutname ${topname}_filled.def -set units 100 ;# write centimicron units into the DEF file +set units 100.0 ;# write centimicron units into the DEF file + +# Input arguments are assumed to be in microns. Convert them to the +# DEF file units. + +set stripewidth [* $units $stripewidth] +set stripepitch [* $units $stripepitch] + +set diexlow 0 +set diexhigh 0 +set dieylow 0 +set dieyhigh 0 #----------------------------------------------------------------- # Open all files for reading and writing #----------------------------------------------------------------- if [catch {open $lefname r} flef] { - puts stderr "Error: can't open file $lefname for input" - return + puts stderr "Error: can't open LEF file $lefname for input" + return } if [catch {open $defname r} fdef] { - puts stderr "Error: can't open file $defname for input" - return + puts stderr "Error: can't open DEF file $defname for input" + return +} + +if {$techlef != ""} { + if [catch {open $techlef r} ftech] { + puts stderr "Warning: can't open LEF tech file $techlef for input" + puts stderr "Will add obstruction layers but no power/ground stripes" + set techlef "" + } +} + +#---------------------------------------------------------------- +# Parse a via section for routing information +# +# (Note: this routine only saves one RECT entry for each layer. +# The intention is only to capture the name of the via, the +# overall dimension, and the layers used.) +#---------------------------------------------------------------- + +proc parse_via {leffile vianame} { + global units + + set viarec [dict create] + set type NONE + dict set viarec name $vianame + + while {[gets $leffile line] >= 0} { + 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] + dict set viarec ${layername} [list $llx $lly $urx $ury] + } elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch viatest] { + if {"$viatest" != "$vianame"} { + puts -nonewline stderr "Unexpected END statement $line while " + puts stderr "reading via $vianame" + } + break + } + } + return $viarec +} + +#---------------------------------------------------------------- +# Parse a layer section for routing information +#---------------------------------------------------------------- + +proc parse_layer {leffile layername} { + global units + + set layerrec [dict create] + set pitch 0 + set type NONE + dict set layerrec name $layername + + if [regexp {[^0-9]*([0-9]+)} $layername lmatch lnum] { + dict set layerrec number $lnum + } else { + dict set layerrec number 0 + } + + while {[gets $leffile line] >= 0} { + if [regexp {[ \t]*TYPE[ \t]+([^ \t]+)[ \t]*;} $line lmatch type] { + dict set layerrec type $type + } 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 + } elseif [regexp {[ \t]*SPACING[ \t]+([^ \t]+)[ \t]*;} $line lmatch space] { + dict set layerrec spacing $space + } 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] + } elseif [regexp {[ \t]*PITCH[ \t]+([^ \t]+)[ \t]*;} $line lmatch pitch] { + set pitch [* $pitch $units] + continue + } elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch layertest] { + if {"$layertest" != "$layername"} { + puts -nonewline stderr "Unexpected END statement $line while " + puts stderr "reading layer $layername" + } + break + } + } + + if {$pitch != 0} { + set orient [dict get $layerrec direction] + if {$orient == "VERTICAL"} { + dict set layerrec xpitch $pitch + dict set layerrec ypitch 0 + } elseif {$orient == "HORIZONTAL"} { + dict set layerrec ypitch $pitch + dict set layerrec xpitch 0 + } + } + return $layerrec +} + +#---------------------------------------------------------------- +# Parse port information for a macro pin from the LEF MACRO block +# Here, used only for power and ground pins. +# Assume that the power and ground ports define one rectangle that +# reaches from end to end of the cell, and record that rectangle. +#---------------------------------------------------------------- + +proc parse_port {pinname macroname leffile ox oy} { + global $macroname units + + set x2 [+ $ox [set ${macroname}(w)]] + + while {[gets $leffile line] >= 0} { + if [regexp {[ \t]*LAYER[ \t]+([^ \t]+)[\t ]*;} $line lmatch layername] { + if {![regexp {.*(\d).*} $layername lmatch layernum]} {set layernum 0} + 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] + if {$llx < $ox} {set llx $ox} + if {$urx > $x2} {set urx $x2} + if {($llx == $ox) && ($urx == $x2)} { + set ${macroname}(${pinname},llx) $llx + set ${macroname}(${pinname},lly) $lly + set ${macroname}(${pinname},urx) $urx + set ${macroname}(${pinname},ury) $ury + } + # To do: If bus is made of more than one rectangle, merge + # leftmost and rightmost rectangles into one entry. + + # NOTE: To do: parse POLYGON records, if this ever comes up in practice. + + } elseif [regexp {[ \t]*END[ \t]*$} $line lmatch] { break } + } +} + +#---------------------------------------------------------------- +# Parse pin information from the LEF MACRO block +#---------------------------------------------------------------- + +proc parse_pin {pinname macroname leffile ox oy} { + global $macroname + + set portuse "" + while {[gets $leffile line] >= 0} { + if [regexp {[ \t]*PORT} $line lmatch] { + if {($portuse == "POWER") || ($portuse == "GROUND") || ($portuse == "")} { + parse_port $pinname $macroname $leffile $ox $oy + } + } elseif [regexp {[ \t]*DIRECTION[ \t]+([^ \t]+)[ \t]*;} $line lmatch porttype] { + set ${macroname}(${pinname},type) $porttype + } elseif [regexp {[ \t]*DIRECTION[ \t]+([^:]+);} $line lmatch porttype] { + set ${macroname}(${pinname},type) $porttype + } elseif [regexp {[ \t]*USE[ \t]+([^ \t]+)[ \t]*;} $line lmatch portuse] { + set ${macroname}(${pinname},use) $portuse + } elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch pintest] { + if {"$pintest" == "$pinname"} { + break + } else { + puts stdout "Unexpected END statement $line while parsing pin $pinname" + } + } + } } #---------------------------------------------------------------- @@ -54,15 +291,15 @@ if [catch {open $defname r} fdef] { #---------------------------------------------------------------- proc skip_section {leffile sectionname} { - while {[gets $leffile line] >= 0} { - if [regexp {[ \t]*END[ \t]+(.+)[ \t]*$} $line lmatch sectiontest] { - if {"$sectiontest" != "$sectionname"} { - puts -nonewline stderr "Unexpected END statement $line " - puts stderr "while reading section $sectionname" - } - break - } - } + while {[gets $leffile line] >= 0} { + if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] { + if {"$sectiontest" != "$sectionname"} { + puts -nonewline stderr "Unexpected END statement $line " + puts stderr "while reading section $sectionname" + } + break + } + } } #---------------------------------------------------------------- @@ -71,100 +308,147 @@ proc skip_section {leffile sectionname} { #---------------------------------------------------------------- proc parse_macro {leffile macroname} { - global $macroname units - - while {[gets $leffile line] >= 0} { - if [regexp {[ \t]*SYMMETRY[ \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 ${macroname}(x) $x - set ${macroname}(y) $y - } elseif [regexp {[ \t]*SIZE[ \t]+(.+)[ \t]+BY[ \t]+(.+)[ \t]*;} \ + global $macroname units + + while {[gets $leffile line] >= 0} { + 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 ${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 ${macroname}(w) $w - set ${macroname}(h) $h - - } elseif [regexp {[ \t]*PIN[ \t]+(.+)[ \t]*$} $line lmatch pinname] { - # The fill cell is not expected to have any usable pins - skip_section $leffile $pinname - } elseif [regexp {[ \t]*END[ \t]+(.+)[ \t]*$} $line lmatch macrotest] { - if {"$macrotest" == "$macroname"} { - break - } else { - puts stderr "Unexpected END statement $line while reading macro $macroname" - } - } - } + set w [expr {int($w * $units)}] + set h [expr {int($h * $units)}] + set ${macroname}(w) $w + set ${macroname}(h) $h + + } elseif [regexp {[ \t]*PIN[ \t]+([^ \t]+)[ \t]*$} $line lmatch pinname] { + # Get power and ground information, the values in the fill cell + # will be used to generate power and ground stripes + parse_pin $pinname $macroname $leffile $x $y + } elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch macrotest] { + if {"$macrotest" == "$macroname"} { + break + } else { + puts stderr "Unexpected END statement $line while reading macro $macroname" + } + } + } +} + +#----------------------------------------------------------------- +# Read a LEF file. Returns a list of fill cells if the LEF file +# was a standard cell set macro file. +#----------------------------------------------------------------- + +proc read_lef {flef fillcell} { + global layers vias + set fillcells {} + while {[gets $flef line] >= 0} { + if [regexp {[ \t]*MACRO[ \t]+([^ \t]+)[ \t]*$} $line lmatch macroname] { + # Parse the "macro" statement + parse_macro $flef $macroname + if {[string first $fillcell $macroname] == 0} { + # Remember this for later if it's a fill cell + lappend fillcells $macroname + } + } elseif [regexp {[ \t]*LAYER[ \t]+([^ \t]+)} $line lmatch layername] { + lappend layers [parse_layer $flef $layername] + } elseif [regexp {[ \t]*VIA[ \t]+([^ \t]+)} $line lmatch vianame] { + lappend vias [parse_via $flef $vianame] + } elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] { + skip_section $flef $viarulename + } elseif [regexp {[ \t]*SITE[ \t]+([^ \t]+)[ \t]*$} $line lmatch sitename] { + skip_section $flef $sitename + } elseif [regexp {[ \t]*UNITS[ \t]*$} $line lmatch] { + skip_section $flef UNITS + } elseif [regexp {[ \t]*SPACING[ \t]*$} $line lmatch] { + skip_section $flef SPACING + } elseif [regexp {[ \t]*END[ \t]+LIBRARY[ \t]*$} $line lmatch] { + break + } elseif [regexp {^[ \t]*#} $line lmatch] { + # Comment line, ignore. + } elseif ![regexp {^[ \t]*$} $line lmatch] { + # Other things we don't care about + set matches 0 + if [regexp {[ \t]*NAMESCASESENSITIVE} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*VERSION} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*BUSBITCHARS} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*DIVIDERCHAR} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*USEMINSPACING} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*CLEARANCEMEASURE} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*MANUFACTURINGGRID} $line lmatch] { + incr matches + } elseif {$fillcell != ""} { + puts stderr "Unexpected input in LEF file: Only macro defs were expected!" + puts -nonewline stdout "Line is: $line" + flush stdout + } + } + } + return $fillcells +} + +#----------------------------------------------------------------- +# Read the tech LEF file for via and layer information +#----------------------------------------------------------------- + +if {$techlef != ""} { + puts stdout "Reading technology LEF file ${techlef}." + read_lef $ftech "" } #----------------------------------------------------------------- -# Read the lef macro file and get the fill cells and their widths +# Read the LEF macro file and get the fill cells and their widths #----------------------------------------------------------------- puts stdout "Reading ${fillcell} macros from LEF file." flush stdout -set fillcells {} - -while {[gets $flef line] >= 0} { - if [regexp {[ \t]*MACRO[ \t]+(.+)[ \t]*$} $line lmatch macroname] { - # Parse the "macro" statement - parse_macro $flef $macroname - if {[string first $fillcell $macroname] == 0} { - # Remember this for later if it's a fill cell - lappend fillcells $macroname - } - } elseif [regexp {[ \t]*LAYER[ \t]+([^ \t]+)} $line lmatch layername] { - skip_section $flef $layername - } elseif [regexp {[ \t]*VIA[ \t]+([^ \t]+)} $line lmatch vianame] { - skip_section $flef $vianame - } elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] { - skip_section $flef $viarulename - } elseif [regexp {[ \t]*SITE[ \t]+(.+)[ \t]*$} $line lmatch sitename] { - skip_section $flef $sitename - } elseif [regexp {[ \t]*UNITS[ \t]*$} $line lmatch] { - skip_section $flef UNITS - } elseif [regexp {[ \t]*SPACING[ \t]*$} $line lmatch] { - skip_section $flef SPACING - } elseif [regexp {[ \t]*END[ \t]+LIBRARY[ \t]*$} $line lmatch] { - break - } elseif [regexp {^[ \t]*#} $line lmatch] { - # Comment line, ignore. - } elseif ![regexp {^[ \t]*$} $line lmatch] { - # Other things we don't care about - set matches 0 - if [regexp {[ \t]*NAMESCASESENSITIVE} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*VERSION} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*BUSBITCHARS} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*DIVIDERCHAR} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*USEMINSPACING} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*CLEARANCEMEASURE} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*MANUFACTURINGGRID} $line lmatch] { - incr matches - } else { - puts stderr "Unexpected input in LEF file: Only macro defs were expected!" - puts -nonewline stdout "Line is: $line" - flush stdout - } - } -} +set fillcells [read_lef $flef $fillcell] # If the macro file doesn't define any fill cells, there's not a -# whole lot we can do. . . +# whole lot we can do, unless we're adding power stripes without +# stretching. if {[llength $fillcells] == 0} { - puts stdout "No fill cells (${fillname}) found in macro file ${lefname}!" - exit 1 + puts stdout "No fill cells (${fillname}) found in macro file ${lefname}!" + if {$dostripes == false || $dostretch == true} {exit 1} +} + +# Get routing grid X pitch from the layer information + +set testpitch 0 +foreach layer $layers { + if {[dict get $layer type] != "ROUTING"} {continue} + set layerpitch [dict get $layer xpitch] + if {$layerpitch > 0} { + if {$testpitch == 0} { + set testpitch $layerpitch + } elseif {$layerpitch < $testpitch} { + set testpitch $layerpitch + } + } +} +set pitchx $testpitch + +# If neither LEF file defined layers, then set "needspecial" to false, +# because there is not enough information to know how to write the +# specialnets section. Flag a warning. + +if {$pitchx == 0} { + puts stderr "No technology layer information in LEF files." + puts stderr "Cannot create physical geometry for power and ground." + set needspecial false } #----------------------------------------------------------------- @@ -174,29 +458,308 @@ if {[llength $fillcells] == 0} { #----------------------------------------------------------------- proc parse_components {deffile rows} { - upvar $rows rdict - while {[gets $deffile line] >= 0} { - if [regexp {[ \t]*END[ \t]+(.+)[ \t]*$} $line lmatch sectiontest] { - if {"$sectiontest" != "COMPONENTS"} { - puts -nonewline stderr "Unexpected END statement $line " - puts stderr "while reading section COMPONENTS" - } - break - } elseif [regexp {[ \t]*-[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\+[ \t]+PLACED[ \t]+\([ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\)[ \t]+([^ \t]+)[ \t]+;} $line lmatch \ + upvar $rows rdict + while {[gets $deffile line] >= 0} { + if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] { + if {"$sectiontest" != "COMPONENTS"} { + puts -nonewline stderr "Unexpected END statement $line " + puts stderr "while reading section COMPONENTS" + } + break + } elseif [regexp {[ \t]*-[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\+[ \t]+PLACED[ \t]+\([ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\)[ \t]+([^ \t]+)[ \t]+;} $line lmatch \ instance macro px py orient] { - if [catch {set row [dict get $rdict $py]}] { - dict set rdict $py [list $px $instance $macro $orient] - } else { - set rowmax [lindex $row 0] - if {$px > $rowmax} { - dict set rdict $py [list $px $instance $macro $orient] + if [catch {set row [dict get $rdict $py]}] { + dict set rdict $py [list $px $instance $macro $orient] + } else { + set rowmax [lindex $row 0] + if {$px > $rowmax} { + dict set rdict $py [list $px $instance $macro $orient] + } + } + } else { + puts -nonewline stderr "Unexpected statement $line " + puts stderr "while reading section COMPONENTS" + } + } +} + +#----------------------------------------------------------------- +# Get the power pin name from a macro +#----------------------------------------------------------------- + +proc get_power {macroname} { + global $macroname + foreach name [array names $macroname] { + if {[set ${macroname}($name)] == "POWER"} { + set cidx [- [string first "," $name] 1] + return [string range $name 0 $cidx] + } + } + return "" +} + +#----------------------------------------------------------------- +# Get the ground pin name from a macro +#----------------------------------------------------------------- + +proc get_ground {macroname} { + global $macroname + foreach name [array names $macroname] { + if {[set ${macroname}($name)] == "GROUND"} { + set cidx [- [string first "," $name] 1] + return [string range $name 0 $cidx] + } + } + return "" +} + +#----------------------------------------------------------------- +# Write entries for SPECIALNETS corresponding to the power and +# ground stripes +#----------------------------------------------------------------- + +proc write_special {stripeinfo stripefills stripewidth stripepattern + rows dieylow dieyhigh fanno} { + global fmin vias layers pitchx + + # Take 1st fill cell and find name of power and ground buses + set fillmacro [lindex $stripefills 0] + global $fillmacro + + # Parse layers for topmost vertical route layer. Unfortunately, + # LEF syntax is too stupid to define which layer is which, so + # we rely on common convention of numbering the metal layers. + set tnum 0 + set topmet 0 + foreach layer $layers { + if {[dict get $layer type] != "ROUTING"} {continue} + if {[dict get $layer direction] == "VERTICAL"} { + set lnum [dict get $layer number] + if {$lnum > $tnum} { + set tnum $lnum + } + } + set lnum [dict get $layer number] + if {$lnum > $topmet} { + set topmet $lnum + } + } + if {$tnum < 3} { + puts stderr "Error: No vertical routing layer at metal 3 or above" + puts stderr "Only posts will be placed, and not connected" + } + + # Create list of route layers in order. + set layerlist {} + for {set i 1} {$i <= $topmet} {incr i} { + foreach layer $layers { + if {[dict get $layer type] == "ROUTING"} { + set lnum [dict get $layer number] + if {$lnum == $i} { + lappend layerlist [dict get $layer name] + } + } + } + } + + # Parse vias for the smallest size via on each metal layer (except top) + # TO-DO: Read VIAGEN entries and generate a via entry the width of the + # power stripe. + + set basevias {} + set viawidths {} + set nvias {} + set blayer "" + set tlayer "" + for {set i 1} {$i <= $topmet} {incr i} { + foreach layer $layers { + if {[dict get $layer type] != "ROUTING"} {continue} + set lnum [dict get $layer number] + if {$lnum == $i} { + set blayer [dict get $layer name] + } elseif {$lnum == [+ $i 1]} { + set tlayer [dict get $layer name] + } + } + set bestvia "" + set bestarea 0 + foreach via $vias { + set areab 0 + set areat 0 + foreach vlayer [dict keys $via] { + if {$vlayer == $blayer} { + set coords [dict get $via $vlayer] + set llx [lindex $coords 0] + set lly [lindex $coords 1] + set urx [lindex $coords 2] + set ury [lindex $coords 3] + set widthb [expr ($urx - $llx)] + set areab [expr $widthb * ($ury - $lly)] + } + if {$vlayer == $tlayer} { + set coords [dict get $via $vlayer] + set llx [lindex $coords 0] + set lly [lindex $coords 1] + set urx [lindex $coords 2] + set ury [lindex $coords 3] + set widtht [expr ($urx - $llx)] + set areat [expr $widtht * ($ury - $lly)] + } + } + if {($areab > 0) && ($areat > 0)} { + set area [max $areab $areat] + set width [max $widthb $widtht] + if {($bestvia == "") || ($area < $bestarea)} { + set bestvia [dict get $via name] + set bestarea $area + set bestwidth $width + } + } + } + lappend basevias $bestvia + lappend viawidths $bestwidth + lappend nvias [expr ($stripewidth / $bestwidth) - 1] + + } + # topmet will be used to index basevias and layerlist, which are + # 0 to tnum - 1, not 1 to tnum. + if {$tnum >= 3} {set topmet [- $tnum 1]} + + set powername [get_power $fillmacro] + + set pllx [set ${fillmacro}(${powername},llx)] + set plly [set ${fillmacro}(${powername},lly)] + set purx [set ${fillmacro}(${powername},urx)] + set pury [set ${fillmacro}(${powername},ury)] + set h [set ${fillmacro}(h)] + set y [/ [+ $plly $pury] 2.0] + + puts $fanno "- ${powername}" + set j 0 + set first true + foreach stripe $stripeinfo { + set smean [lindex $stripe 0] + set slow [lindex $stripe 1] + set shigh [lindex $stripe 2] + + if {[- $shigh $slow] > 0} { + set smean [/ [+ $shigh $slow] 2.0] + } + # Make sure smean is on pitch + set nw [expr int($smean / $fmin)] + set smean [* $nw $fmin] + + set slow [- $smean [/ $stripewidth 2.0]] + set shigh [+ $smean [/ $stripewidth 2.0]] + + set pattern [string index $stripepattern $j] + if {$pattern == "P"} { + set drows [dict keys $rows] + foreach rowy $drows { + set orient [lindex [dict get $rows $rowy] 3] + if {$orient == "S" || $orient == "FS"} { + set ay [- $h $y] + } else { + set ay $y + } + for {set i 0} {$i < $topmet} {incr i} { + set ry [+ $rowy $ay] + set layer [lindex $layerlist $i] + set via [lindex $basevias $i] + set vw [lindex $viawidths $i] + set nv [lindex $nvias $i] + set x1 $slow + if $first { + puts -nonewline $fanno "+ ROUTED " + set first false + } else { + puts -nonewline $fanno " NEW " + } + puts $fanno "$layer $vw ( $shigh $ry ) ( $x1 * )" + for {set k 0} {$k < $nv} {incr k} { + set x2 [+ $x1 $vw] + puts $fanno " NEW $layer $vw ( $x1 $ry ) ( $x2 * ) $via" + set x1 $x2 + } + } + } + # At the end, put top vertical metal stripe from top to bottom + if {$tnum >= 3} { + set layer [lindex $layerlist [- $tnum 1]] + puts $fanno " NEW $layer $stripewidth ( $smean $dieylow ) ( * $dieyhigh )" } - } - } else { - puts -nonewline stderr "Unexpected statement $line " - puts stderr "while reading section COMPONENTS" - } - } + } + incr j + } + puts $fanno " ;" + + set groundname [get_ground $fillmacro] + + set gllx [set ${fillmacro}(${groundname},llx)] + set glly [set ${fillmacro}(${groundname},lly)] + set gurx [set ${fillmacro}(${groundname},urx)] + set gury [set ${fillmacro}(${groundname},ury)] + set w [- $gury $glly] + set y [/ [+ $glly $gury] 2.0] + + puts $fanno "- ${groundname}" + set j 0 + set first true + foreach stripe $stripeinfo { + set smean [lindex $stripe 0] + set slow [lindex $stripe 1] + set shigh [lindex $stripe 2] + + if {[- $shigh $slow] > 0} { + set smean [/ [+ $shigh $slow] 2.0] + } + # Make sure smean is on pitch + set nw [expr int($smean / $fmin)] + set smean [* $nw $fmin] + + set slow [- $smean [/ $stripewidth 2.0]] + set shigh [+ $smean [/ $stripewidth 2.0]] + + set pattern [string index $stripepattern $j] + if {$pattern == "G"} { + set drows [dict keys $rows] + foreach rowy $drows { + set orient [lindex [dict get $rows $rowy] 3] + if {$orient == "S" || $orient == "FS"} { + set ay [- $h $y] + } else { + set ay $y + } + for {set i 0} {$i < $topmet} {incr i} { + set ry [+ $rowy $ay] + set layer [lindex $layerlist $i] + set via [lindex $basevias $i] + set vw [lindex $viawidths $i] + set nv [lindex $nvias $i] + set x1 $slow + if $first { + puts -nonewline $fanno "+ ROUTED " + set first false + } else { + puts -nonewline $fanno " NEW " + } + puts $fanno "$layer $vw ( $shigh $ry ) ( $x1 * )" + for {set k 0} {$k < $nv} {incr k} { + set x2 [+ $x1 $vw] + puts $fanno " NEW $layer $vw ( $x1 $ry ) ( $x2 * ) $via" + set x1 $x2 + } + } + } + # At the end, put top vertical metal stripe from top to bottom + if {$tnum >= 3} { + set layer [lindex $layerlist [- $tnum 1]] + puts $fanno " NEW $layer $stripewidth ( $smean $dieylow ) ( * $dieyhigh )" + } + } + incr j + } + puts $fanno " ;" } #----------------------------------------------------------------- @@ -208,54 +771,54 @@ puts stdout "Reading DEF file ${defname}. . ." flush stdout while {[gets $fdef line] >= 0} { - if [regexp {[ \t]*COMPONENTS[ \t]+([^ \t]+)[ \t]*;} $line lmatch number] { - set rows [dict create] - # Parse the "COMPONENTS" statement - parse_components $fdef rows - } elseif [regexp {[ \t]*NETS[ \t]+([^ \t]+)} $line lmatch netnums] { - skip_section $fdef NETS - } elseif [regexp {[ \t]*SPECIALNETS[ \t]+([^ \t]+)} $line lmatch netnums] { - skip_section $fdef SPECIALNETS - } elseif [regexp {[ \t]*PINS[ \t]+([^ \t]+)} $line lmatch pinnum] { - skip_section $fdef PINS - } elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] { - skip_section $fdef $viarulename - } elseif [regexp {[ \t]*VIA[ \t]+(.+)[ \t]*$} $line lmatch sitename] { - skip_section $fdef $sitename - } elseif [regexp {[ \t]*END[ \t]+DESIGN[ \t]*$} $line lmatch] { - break - } elseif [regexp {^[ \t]*#} $line lmatch] { - # Comment line, ignore. - } elseif ![regexp {^[ \t]*$} $line lmatch] { - # Other things we don't care about - set matches 0 - if [regexp {[ \t]*NAMESCASESENSITIVE} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*VERSION} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*BUSBITCHARS} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*DIVIDERCHAR} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*USEMINSPACING} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*CLEARANCEMEASURE} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*MANUFACTURINGGRID} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*UNITS} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*DESIGN} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*DIEAREA} $line lmatch] { - incr matches - } elseif [regexp {[ \t]*TRACKS} $line lmatch] { - incr matches - } else { - puts stderr "Unexpected input in DEF file:" - puts stdout "Line is: $line" - } - } + if [regexp {[ \t]*COMPONENTS[ \t]+([^ \t]+)[ \t]*;} $line lmatch number] { + set rows [dict create] + # Parse the "COMPONENTS" statement + parse_components $fdef rows + } elseif [regexp {[ \t]*NETS[ \t]+([^ \t]+)} $line lmatch netnums] { + skip_section $fdef NETS + } elseif [regexp {[ \t]*SPECIALNETS[ \t]+([^ \t]+)} $line lmatch netnums] { + skip_section $fdef SPECIALNETS + } elseif [regexp {[ \t]*PINS[ \t]+([^ \t]+)} $line lmatch pinnum] { + skip_section $fdef PINS + } elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] { + skip_section $fdef $viarulename + } elseif [regexp {[ \t]*VIA[ \t]+([^ \t]+)[ \t]*$} $line lmatch vianame] { + lappend vias [parse_via $fdef $vianame] + } elseif [regexp {[ \t]*END[ \t]+DESIGN[ \t]*$} $line lmatch] { + break + } elseif [regexp {^[ \t]*#} $line lmatch] { + # Comment line, ignore. + } elseif ![regexp {^[ \t]*$} $line lmatch] { + # Other things we don't care about + set matches 0 + if [regexp {[ \t]*NAMESCASESENSITIVE} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*VERSION} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*BUSBITCHARS} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*DIVIDERCHAR} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*USEMINSPACING} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*CLEARANCEMEASURE} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*MANUFACTURINGGRID} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*UNITS} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*DESIGN} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*DIEAREA} $line lmatch] { + incr matches + } elseif [regexp {[ \t]*TRACKS} $line lmatch] { + incr matches + } else { + puts stderr "Unexpected input in DEF file:" + puts stdout "Line is: $line" + } + } } close $flef @@ -265,7 +828,7 @@ close $fdef set fillwidths {} foreach macro $fillcells { - lappend fillwidths [list $macro [subst \$${macro}(w)]] + lappend fillwidths [list $macro [set ${macro}(w)]] } set fillwidths [lsort -decreasing -index 1 $fillwidths] @@ -273,20 +836,23 @@ set fillwidths [lsort -decreasing -index 1 $fillwidths] # to get the row end X value dict for {row rowvals} $rows { - set xmax [lindex $rowvals 0] - set macro [lindex $rowvals 2] - set xmax [+ $xmax [subst \$${macro}(w)]] - set rowvals [lreplace $rowvals 0 0 $xmax] - dict set rows $row $rowvals + set xmax [lindex $rowvals 0] + set macro [lindex $rowvals 2] + set xmax [+ $xmax [set ${macro}(w)]] + set rowvals [lreplace $rowvals 0 0 $xmax] + dict set rows $row $rowvals } -# Find longest row +# Find number of rows and longest row +set numrows 0 set rowmax 0 dict for {row rowvals} $rows { - set xmax [lindex $rowvals 0] - if {$xmax > $rowmax} {set rowmax $xmax} + incr numrows + set xmax [lindex $rowvals 0] + if {$xmax > $rowmax} {set rowmax $xmax} } -puts stdout "Longest row is width $rowmax" +puts stdout "Number of rows is $numrows" +puts stdout "Longest row has width [/ $rowmax $units] um" # Now, for each row, find the difference between the row end and row max, # and create a list of how many of each fill macro it takes to fill the @@ -294,21 +860,126 @@ puts stdout "Longest row is width $rowmax" set numfills 0 dict for {row rowvals} $rows { - set xmax [lindex $rowvals 0] - set xd [- $rowmax $xmax] - set fills {} - foreach fillset $fillwidths { - set fw [lindex $fillset 1] - set fn [floor [/ $xd $fw]] - lappend fills [list [lindex $fillset 0] [int $fn]] - set xd [- $xd [* $fn $fw]] - incr numfills [int $fn] - } - lappend rowvals $fills - dict set rows $row $rowvals + set xmax [lindex $rowvals 0] + set xd [- $rowmax $xmax] + set fills {} + foreach fillset $fillwidths { + set fw [lindex $fillset 1] + set fn [floor [/ $xd $fw]] + lappend fills [list [lindex $fillset 0] [int $fn]] + set xd [- $xd [* $fn $fw]] + incr numfills [int $fn] + } + lappend rowvals $fills + dict set rows $row $rowvals } set numcomps [+ $number $numfills] +# Analyze design for power striping + +if {$dostripes && ($rowmax > $stripepitch)} { + + # What sizes of fill make up the stripe width, to the nearest unit cell? + set stripefills {} + + set xtot 0 + while {1} { + set fidx 0 + set fmax 0 + set fmin -1 + set i 0 + foreach fillset $fillwidths { + set fw [lindex $fillset 1] + set ftest [+ $fw $xtot] + if {($ftest <= $stripewidth) && ($ftest > $fmax)} { + set fmax $fw + set fidx $i + } + if {$fmin < 0} { + set fmin $fw + } elseif {$fmin > $fw} { + set fmin $fw + } + incr i + } + if {$fmax == 0} { + break + } + 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] + } + # 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 + # size. + set stripewidth $xtot + + # If pitchx was given in the tech LEF file, then use that as the + # minimum pitch to snap values to. If not, use the minimum fill + # cell size. + if {$pitchx > 0} {set fmin $pitchx} + + # Reduce stripepitch to a multiple of the minimum cell size + # (note: should change this to a multiple of track pitch) + set nw [expr int($stripepitch / $fmin)] + set stripepitch [* $nw $fmin] + + # How many stripes need to be added? Allow +/-50% (max) to + # accomodate layout widths that are not a multiple of the + # stripe pitch. "numstripes" does not include power stripes + # outside of the track area on right and left sides. + + if $dostretch { + set numstripes [expr int($rowmax / $stripepitch) - 1] + while true { + set rowmaxeff [+ $rowmax [* $numstripes $stripewidth]] + set newstripes [expr int(($rowmaxeff + 0.5 * $stripepitch) / $stripepitch) - 1] + if {$newstripes == $numstripes} {break} + set numstripes $newstripes + } + if {$numstripes == 0} { + set stripewidth 0 + set dostripes false + puts stdout "addspacers: No room for stripes, none added." + } else { + set stripeoffset [expr $stripepitch - (($stripepitch * ($numstripes + 1)) - $rowmax) / 2] + set nw [expr int($stripeoffset / $fmin)] + set stripeoffset [* $nw $fmin] + + # Number of components increases by number of fill cells per stripe * + # number of stripes * number of rows. + set numfills [* [llength $stripefills] $numstripes $numrows] + set numcomps [+ $numcomps $numfills] + puts stdout "addspacers: Inserting $numstripes stripes of width [/ $stripewidth $units] um ($stripewidthreq um requested)" + puts stdout " Pitch [/ $stripepitch $units] um, offset [/ $stripeoffset $units] um" + } + puts stdout "stretch: Number of components is $numcomps" + } else { + set numstripes [expr int(($rowmax + 0.5 * $stripepitch) / $stripepitch) - 1] + if {$numstripes == 0} { + set stripewidth 0 + set dostripes false + puts stdout "addspacers: No room for stripes, none added." + } else { + set stripeoffset [expr $stripepitch - (($stripepitch * ($numstripes + 1)) - $rowmax) / 2] + set nw [expr int($stripeoffset / $fmin)] + set stripeoffset [* $nw $fmin] + puts stdout "addspacers: Inserting $numstripes stripes of width [/ $stripewidth $units] um ($stripewidthreq um requested)" + puts stdout " Pitch [/ $stripepitch $units] um, offset [/ $stripeoffset $units] um" + } + } + + # Analyze design for the number of power bus stripes to add + + # Duplicate "stripepattern" to (at least) the length of (numstripes + 2) + set pattern "" + while {[string length $pattern] < [+ $numstripes 2]} { + append pattern $stripepattern + } + set stripepattern $pattern +} + # Diagnostic puts stdout "Analysis of DEF file:" puts stdout "Number of components = $number" @@ -318,8 +989,8 @@ puts stdout "Number of rows = [llength [dict keys $rows]]" set fdef [open $defname r] if [catch {open $defoutname w} fanno] { - puts stderr "Error: can't open file $defoutname for output" - return + puts stderr "Error: can't open file $defoutname for output" + return } #----------------------------------------------------------------- @@ -327,44 +998,335 @@ if [catch {open $defoutname w} fanno] { # of each row #----------------------------------------------------------------- +# Each stripe has a record of center point and width of free space from +# cell top to bottom (list over rows) +set stripeinfo {} +# Each row has a record of X position of the stripe fill (list over stripes). +set rowstripeinfo {} + +set needspecial $dostripes + +if {$dostretch == false} { + # If not stretching the area under the power stripes with fill, the + # stripeinfo record needs to be filled with the position of each + # stripe so that write_special gets this information + + set xpos $stripeoffset + set ns 0 + while {$ns < $numstripes} { + set sinfo $xpos + lappend sinfo [- $xpos [/ $stripewidth 2.0]] + lappend sinfo [+ $xpos [/ $stripewidth 2.0]] + lappend stripeinfo $sinfo + set xpos [+ $xpos $stripepitch] + incr ns + } +} + while {[gets $fdef line] >= 0} { - if [regexp {[ \t]*COMPONENTS[ \t]+([^ \t]+)[ \t]*;} $line lmatch number] { - puts $fanno "COMPONENTS $numcomps ;" - set r 0 - while {[gets $fdef line] >= 0} { - puts $fanno $line - if [regexp {[ \t]*END[ \t]+(.+)[ \t]*$} $line lmatch sectiontest] { - break - } elseif [regexp {[ \t]*-[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\+[ \t]+PLACED[ \t]+\([ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\)[ \t]+([^ \t]+)[ \t]+;} $line lmatch \ - instance macro px py orient] { - # Check if there is a match to the last instance in the row - set rowvals [dict get $rows $py] - set rowinst [lindex $rowvals 1] - if {[string equal $instance $rowinst]} { - incr r - set xpos [lindex $rowvals 0] - set fills [lindex $rowvals 4] - # Get orientation of row (N or S); - # remove "F" if last cell was flipped - set orient [string index [lindex $rowvals 3] end] - foreach fpair $fills { - set fmacro [lindex $fpair 0] - set fw [subst \$${fmacro}(w)] - set fn [lindex $fpair 1] - for {set i 1} {$i <= $fn} {incr i} { - puts $fanno "- ${fmacro}_${r}_${i} ${fmacro} + PLACED ( $xpos $py ) $orient ;" - set xpos [+ $xpos $fw] - } - } + if [regexp {[ \t]*DIEAREA[ \t]*\([ \t]*([^ \t]+)[ \t]+([^ \t]+)[ \t]*\)[ \t]*\([ \t]*([^ \t]+)[ \t]+([^ \t]+)[ \t]*\)[ \t]*;} $line lmatch \ + diexlow dieylow diexhigh dieyhigh] { + + # Add stripe width to die area total + if $dostretch { + set diexhigh [+ $diexhigh [* $stripewidth $numstripes]] + puts $fanno "DIEAREA ( $diexlow $dieylow ) ( $diexhigh $dieyhigh ) ;" + } else { + puts $fanno $line + } + + } elseif [regexp {[ \t]*TRACKS[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+DO[ \t]+([^ \t]+)[ \t]+STEP[ \t]+([^ \t]+)[ \t]+LAYER[ \t]+([^ \t]+)[ \t]*;} $line lmatch \ + orient low num step layer] { + + if {$dostretch && ($stripewidth > 0) && ($orient == "X")} { + # Add number of tracks to cover the added area of the stripes + set ntracks [expr int(($stripewidth * $numstripes) / $step)] + set num [+ $num $ntracks] + puts $fanno "TRACKS X $low DO $num STEP $step LAYER $layer ;" + } else { + puts $fanno $line + } + + } elseif [regexp {[ \t]*PINS[ \t]+([^ \t]+)[ \t]*;} $line lmatch number] { + + # Add space to pins to match the addition of stripes + set extrapins false + if {$dostripes} { + + # Note: This duplicates code in write_special. Needs to be + # consolidated into one routine. + + set tnum 0 + foreach layer $layers { + if {[dict get $layer type] != "ROUTING"} {continue} + if {[dict get $layer direction] == "VERTICAL"} { + set lnum [dict get $layer number] + if {$lnum > $tnum} { + set tnum $lnum + set toplayer [dict get $layer name] + } + } + } + if {$tnum >= 3} {set extrapins true} + } + if {$extrapins} { + + puts $fanno "PINS [+ $number 2] ;" + set i 0 + set donepower false + set doneground false + set fillmacro [lindex $stripefills 0] + foreach stripe ${stripeinfo} { + + set stripetype [string index $stripepattern $i] + set smean [lindex $stripe 0] + set slow [lindex $stripe 1] + set shigh [lindex $stripe 2] + if {[- $shigh $slow] > 0} { + set smean [/ [+ $shigh $slow] 2.0] + } + set nw [expr int($smean / $fmin)] + set smean [* $nw $fmin] + set hw [/ $stripewidth 2.0] + set qw [/ $hw 2.0] + set slow [- $smean $hw] + set shigh [+ $smean $hw] + set cy [+ $dieylow $qw] + + if {!$donepower && ($stripetype == "P")} { + set powername [get_power $fillmacro] + puts $fanno "- $powername + NET $powername" + puts $fanno " + LAYER $toplayer ( -$hw -$qw ) ( $hw $qw )" + puts $fanno " + PLACED ( $smean $cy ) N ;" + set donepower true + } elseif {!$doneground && ($stripetype == "G")} { + set groundname [get_ground $fillmacro] + puts $fanno "- $groundname + NET $groundname" + puts $fanno " + LAYER $toplayer ( -$hw -$qw ) ( $hw $qw )" + puts $fanno " + PLACED ( $smean $cy ) N ;" + set doneground true + } + incr i + if {$donepower && $doneground} {break} + } + } else { + puts $fanno $line + } + + if {$dostripes && $dostretch} { + while {[gets $fdef line] >= 0} { + if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] { + puts $fanno $line + break + } elseif [regexp {[ \t]*\+[ \t]+PLACED[ \t]+\([ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\)[ \t]+([^ \t]+)[ \t]+;} $line lmatch \ + px py orient] { + + # Check if instance px exceeds xbreak and adjust accordingly + if {$px > $stripepitch} { + set ns [expr int($px / $stripepitch)] + if {$ns > $numstripes} {set ns $numstripes} + set nw [* $ns $stripewidth] + set xpos [+ $px $nw] + puts $fanno " + PLACED ( $xpos $py ) $orient ;" + } else { + puts $fanno $line + } + } else { + puts $fanno $line + } } - } - } - } else { - puts $fanno $line - } + } + + } elseif [regexp {[ \t]*COMPONENTS[ \t]+([^ \t]+)[ \t]*;} $line lmatch number] { + puts $fanno "COMPONENTS $numcomps ;" + set r 0 + set s 0 + set xadd 0 + if {$dostripes && $dostretch} { + set xbreak $stripeoffset + } else { + set xbreak $rowmax + } + while {[gets $fdef line] >= 0} { + if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] { + puts $fanno $line + + if {$dostripes} { + # Finish computing values of internal stripes + # replace total with mean, and copy the bounds values + set newinfo {} + foreach stripe ${stripeinfo} { + set smean [lindex $stripe 0] + set slow [lindex $stripe 1] + set shigh [lindex $stripe 2] + set smean [/ $smean $numrows] + lappend newinfo [list $smean $slow $shigh] + } + set stripeinfo $newinfo + } + break + + } elseif [regexp {[ \t]*-[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\+[ \t]+PLACED[ \t]+\([ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+\)[ \t]+([^ \t]+)[ \t]+;} $line lmatch \ + instance macro px py orient] { + + # Quick check if the right side of instance exceeds xbreak and + # if placing the stripe before the instance will put the stripe + # closer to the ideal position. + + set pw [set ${macro}(w)] + set right [+ $px $pw] + + # Check if instance position px exceeds xbreak and adjust accordingly + if {($px >= $xbreak) || ([- $xbreak $px] < [- [+ $px $pw] $xbreak])} { + + # Add to row stripe info + lappend rowstripeinfo [+ $px $xadd] + + # Add fill in the stripe area + set i 0 + foreach fmacro $stripefills { + set fw [set ${fmacro}(w)] + set xpos [+ $px $xadd] + puts $fanno "- ${fmacro}_${r}_${s}_${i} ${fmacro} + PLACED ( $xpos $py ) $orient ;" + set xadd [+ $xadd $fw] + incr i + } + if {$s < $numstripes} { + set xbreak [+ $xbreak $stripepitch] + incr s + } else { + set xbreak $rowmax + } + } + set px [+ $px $xadd] + puts $fanno "- $instance $macro + PLACED ( $px $py ) $orient ;" + + # Check if there is a match to the last instance in the row + set rowvals [dict get $rows $py] + set rowinst [lindex $rowvals 1] + if {[string equal $instance $rowinst]} { + incr r + set xpos [lindex $rowvals 0] + if {$dostripes && $dostretch} { + # puts stdout "stripewidth = $stripewidth" + # puts stdout "numstripes = $numstripes" + # puts stdout "xpos in = $xpos" + set xpos [+ $xpos [* $stripewidth $numstripes]] + # puts stdout "xpos out = $xpos" + } + set fills [lindex $rowvals 4] + # Get orientation of row (N or S); + # remove "F" if last cell was flipped + set orient [string index [lindex $rowvals 3] end] + foreach fpair $fills { + set fmacro [lindex $fpair 0] + set fw [set ${fmacro}(w)] + set fn [lindex $fpair 1] + for {set i 1} {$i <= $fn} {incr i} { + puts $fanno "- ${fmacro}_${r}_${i} ${fmacro} + PLACED ( $xpos $py ) $orient ;" + set xpos [+ $xpos $fw] + } + } + # Add rowstripeinfo values to stripeinfo + # Reset xbreak, xadd, and rowstripeinfo + if {$dostretch && ($xadd > 0)} { + set xadd 0 + set xbreak $stripeoffset + set i 0 + set newstripeinfo {} + if {[llength $stripeinfo] == 0} { + foreach sx $rowstripeinfo { + set slow $sx + set shigh [+ $sx $stripewidth] + set smean [+ $sx [/ $stripewidth 2.0]] + lappend newstripeinfo [list $smean $slow $shigh] + } + } else { + foreach sx $rowstripeinfo { + set sinfo [lindex $stripeinfo $i] + set slow [max [lindex $sinfo 1] $sx] + set shigh [min [lindex $sinfo 2] [+ $sx $stripewidth]] + set smean [+ [lindex $sinfo 0] [+ $sx [/ $stripewidth 2.0]]] + lappend newstripeinfo [list $smean $slow $shigh] + incr i + } + } + set stripeinfo $newstripeinfo + set rowstripeinfo {} + set s 0 + } + } + } + } + } elseif [regexp {[ \t]*END[ \t]+DESIGN} $line lmatch] { + if {$needspecial == true} { + puts $fanno "SPECIALNETS 2 ;" + write_special $stripeinfo $stripefills $stripewidth $stripepattern \ + $rows $dieylow $dieyhigh $fanno + puts $fanno "END SPECIALNETS" + } + puts $fanno $line + } elseif [regexp {[ \t]*SPECIALNETS[ \t]+([^ \t]+)} $line lmatch netnums] { + if {$needspecial == true} { + puts $fanno "SPECIALNETS [+ 2 $netnums] ;" + write_special $stripeinfo $stripefills $stripewidth $stripepattern \ + $rows $dieylow $dieyhigh $fanno + set needspecial false + } + } else { + puts $fanno $line + } } close $fanno close $fdef +if {$techlef != ""} { + close $ftech +} + +#----------------------------------------------------------------- +# If there is a ${topname}.obs file, read it in and adjust the +# X values to account for the spacers. +#----------------------------------------------------------------- + +if {$dostripes && $dostretch} { + + # Cast values back into microns, which are the units used by the .obs file + set stripewidth [/ $stripewidth $units] + set stripepitch [/ $stripepitch $units] + set stripeoffset [/ $stripeoffset $units] + + if [catch {open ${topname}.obs r} infobs] { + # No .obs file, no action needed. + puts stdout "No file ${topname}.obs, so no adjustment required." + return + } + if [catch {open ${topname}.obsx w} outfobs] { + puts stout "Error: Cannot open file ${topname}.obsx for writing!" + return + } + 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] { + if {$xlow > $stripeoffset} { + set ns [expr int(($xlow - $stripeoffset + $stripepitch) / $stripepitch)] + if {$ns > $numstripes} {set ns $numstripes} + set nw [* $ns $stripewidth] + set xlow [+ $xlow $nw] + } + if {$xhigh > $stripeoffset} { + set ns [expr int(($xlow - $stripeoffset + $stripepitch) / $stripepitch)] + if {$ns > $numstripes} {set ns $numstripes} + set nw [* $ns $stripewidth] + set xhigh [+ $xhigh $nw] + } + puts $outfobs "obstruction $xlow $ylow $xhigh $yhigh $layer" + } else { + puts $outfobs $outline + } + } + close $outfobs + close $infobs +} puts stdout "Done with addspacers.tcl" diff --git a/scripts/placement.sh b/scripts/placement.sh index 5425720..5116f3c 100755 --- a/scripts/placement.sh +++ b/scripts/placement.sh @@ -167,7 +167,7 @@ endif # information to GrayWolf for a final placement pass using fill to # break up congested areas. -if ( -f ${rootname}.acel && ( -M ${rootname}.acel > -M ${rootname}.cel )) then +if ( -f ${rootname}.acel && ( -M ${rootname}.acel >= -M ${rootname}.cel )) then cp ${rootname}.cel ${rootname}.cel.bak mv ${rootname}.acel ${rootname}.cel set final = 1 @@ -208,7 +208,7 @@ endif # powerbus.tcl creates a .acel file if successful. If not, then # leave the .cel file in place -if ( -f ${rootname}.acel && ( -M ${rootname}.acel > -M ${rootname}.cel )) then +if ( -f ${rootname}.acel && ( -M ${rootname}.acel >= -M ${rootname}.cel )) then cp ${rootname}.cel ${rootname}.cel.bak mv ${rootname}.acel ${rootname}.cel endif @@ -324,8 +324,35 @@ if ($makedef == 1) then exit 1 endif - # Copy the .def file to a backup called "unroute" (temporary) - cp ${rootname}.def ${rootname}_unroute.def + #--------------------------------------------------------------------- + # Add spacer cells to create a straight border on the right side + #--------------------------------------------------------------------- + + if ( -f ${scriptdir}/addspacers.tcl ) then + + if ( !( ${?addspacers_options} )) then + set addspacers_options = "" + endif + + echo "Running addspacers.tcl ${addspacers_options} ${rootname} ${lefpath} ${fillcell}" |& tee -a ${synthlog} + + ${scriptdir}/addspacers.tcl ${addspacers_options} \ + ${rootname} ${lefpath} ${fillcell} >>& ${synthlog} + if ( -f ${rootname}_filled.def ) then + mv ${rootname}_filled.def ${rootname}.def + # Copy the .def file to a backup called "unroute" + cp ${rootname}.def ${rootname}_unroute.def + endif + + if ( -f ${rootname}.obsx ) then + # If addspacers annotated the .obs (obstruction) file, then + # overwrite the original. + mv ${rootname}.obsx ${rootname}.obs + endif + else + # Copy the .def file to a backup called "unroute" + cp ${rootname}.def ${rootname}_unroute.def + endif # 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 @@ -351,7 +378,7 @@ if ($makedef == 1) then echo "read_lef ${techlefpath}" >> ${rootname}.cfg endif echo "read_lef ${lefpath}" >> ${rootname}.cfg - echo "catch layers ${route_layers}" >> ${rootname}.cfg + echo "catch {layers ${route_layers}}" >> ${rootname}.cfg if ( ${?via_pattern} ) then echo "" >> ${rootname}.cfg echo "via pattern ${via_pattern}" >> ${rootname}.cfg @@ -386,6 +413,7 @@ if ($makedef == 1) then endif # Add obstruction fence around design, created by place2def.tcl + # and modified by addspacers.tcl if ( -f ${rootname}.obs ) then cat ${rootname}.obs >> ${rootname}.cfg @@ -407,14 +435,10 @@ if ($makedef == 1) then cat ${rootname}.cfg2 >> ${rootname}.cfg else if (${scripting} == "T") then - if (${final} == 0) then - echo "qrouter::congestion_route ${rootname}.cinfo" >> ${rootname}.cfg - else - echo "qrouter::standard_route" >> ${rootname}.cfg - # Standard route falls back to the interpreter on failure, - # so make sure that qrouter actually exits. - echo "quit" >> ${rootname}.cfg - endif + echo "qrouter::standard_route" >> ${rootname}.cfg + # Standard route falls back to the interpreter on failure, + # so make sure that qrouter actually exits. + echo "quit" >> ${rootname}.cfg endif endif @@ -461,7 +485,7 @@ if ($makedef == 1) then ${rootname}_anno.blif > ${rootname}.rtlnopwr.v echo "Running blif2BSpice." |& tee -a ${synthlog} - ${bindir}/blif2BSpice -p ${vddnet} -g ${gndnet} -l \ + ${bindir}/blif2BSpice -i -p ${vddnet} -g ${gndnet} -l \ ${spicepath} ${rootname}_anno.blif \ > ${rootname}.spc @@ -494,24 +518,6 @@ if ($makedef == 1) then endif #--------------------------------------------------- -# 3) Add spacer cells to create a straight border on -# the right side -#--------------------------------------------------- - -if ($makedef == 1) then - if ( -f ${scriptdir}/addspacers.tcl ) then - echo "Running addspacers.tcl" - ${scriptdir}/addspacers.tcl ${rootname} ${lefpath} \ - $fillcell >>& ${synthlog} - if ( -f ${rootname}_filled.def ) then - mv ${rootname}_filled.def ${rootname}.def - # Copy the .def file to a backup called "unroute" (final) - cp ${rootname}.def ${rootname}_unroute.def - endif - endif -endif - -#--------------------------------------------------- # 4) Remove working files (except for the main # output files .pin, .pl1, and .pl2 #--------------------------------------------------- diff --git a/scripts/qflow.sh.in b/scripts/qflow.sh.in index b78cd20..d6a0436 100644 --- a/scripts/qflow.sh.in +++ b/scripts/qflow.sh.in @@ -292,6 +292,7 @@ if ( ! -f ${userfile} ) then echo "# -------------------------------------------" >> ${userfile} echo "# set initial_density = " >> ${userfile} echo "# set graywolf_options = " >> ${userfile} + echo '# set addspacers_options = "-stripe 5 200 PG -nostretch"' >> ${userfile} echo "" >> ${userfile} echo "# Router command options:" >> ${userfile} echo "# -------------------------------------------" >> ${userfile} @@ -310,45 +311,45 @@ endif if ($dosynth == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/synthesize.sh ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/synthesize.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($doplace == 0) then echo -n "# " >> ${execfile} 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}" >> ${execfile} +echo "${scriptdir}/placement.sh -d ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($dosta == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/vesta.sh ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/vesta.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($doroute == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/router.sh ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/router.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($dodecongest == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/placement.sh -f -d ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/placement.sh -f -d ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($dodecongest == 0) then echo -n "# " >> ${execfile} endif -echo -n "${scriptdir}/router.sh ${projectpath} ${modulename}" >> ${execfile} +echo -n "${scriptdir}/router.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} echo ' $status' >> ${execfile} if ($doclean == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/cleanup.sh ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/cleanup.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} if ($dodisplay == 0) then echo -n "# " >> ${execfile} endif -echo "${scriptdir}/display.sh ${projectpath} ${modulename}" >> ${execfile} +echo "${scriptdir}/display.sh ${projectpath} ${modulename} || exit 1" >> ${execfile} if ( $actions == 0 ) then echo "No actions specified on command line;" diff --git a/scripts/router.sh b/scripts/router.sh index 93f0b7d..0cca2e6 100755 --- a/scripts/router.sh +++ b/scripts/router.sh @@ -118,7 +118,7 @@ if (${scripting} == "T") then ${bindir}/qrouter -noc -s ${rootname}.cfg ${qrouter_options} \ |& tee -a ${synthlog} | \ grep - -e fail -e Progress -e remaining.\*00\$ \ - -e remaining:\ \[1-9\]0\\\?\$ + -e remaining:\ \[1-9\]0\\\?\$ -e \\\*\\\*\\\* else #------------------------------------------------------------------ @@ -131,7 +131,7 @@ else ${bindir}/qrouter -c ${rootname}.cfg -p ${vddnet} -g ${gndnet} \ ${qrouter_options} ${rootname} |& tee -a ${synthlog} | \ grep - -e fail -e Progress -e remaining.\*00\$ \ - -e remaining:\ \[1-9\]0\\\?\$ + -e remaining:\ \[1-9\]0\\\?\$ -e \\\*\\\*\\\* endif #--------------------------------------------------------------------- diff --git a/scripts/spi2xspice.py b/scripts/spi2xspice.py new file mode 100755 index 0000000..3544f53 --- /dev/null +++ b/scripts/spi2xspice.py @@ -0,0 +1,762 @@ +#!/bin/env python3 +# +# spi2xspice.py +# +# Simple script to convert a standard-cell SPICE subcircuit (e.g., one created +# by qflow) into a version replacing all the standard cells with XSPICE +# primitives. This works in conjunction with lib2xspice.py, which converts +# a liberty file for a standard cell set into a set of XSPICE models using +# d_lut, d_genlut, d_dff, and d_dlatch. +# +# Written by Tim Edwards +# efabless, inc. 2017 +# May 17, 2017 +# This script is in the public domain +#-------------------------------------------------------------------------- + +import re +import sys +import os + +def read_spice_lib(filein, celldefs, debug): + # Read a spice library of standard cell definitions + + spicedefs = {} + + with open(filein, 'r') as ifile: + spitext = ifile.read() + + increx = re.compile('^[^\*]*[ \t]*.include[ \t]+([^ \t]+).*$', re.IGNORECASE) + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+)(.*)$', re.IGNORECASE) + endsrex = re.compile('^[^\*]*[ \t]*.ends.*$', re.IGNORECASE) + comrex = re.compile('^[\*]+.*$', re.IGNORECASE) + + # Merge continuation lines in input + spilines = spitext.replace('\n+', ' ').splitlines() + if debug: + print("Reading spice file, " + str(len(spilines)) + " lines.") + + insub = False + for line in spilines: + + # All comment lines are ignored + cmatch = comrex.match(line) + if cmatch: + continue + + imatch = increx.match(line) + if imatch: + # Check if filename needs same prefix as current file + if not os.path.exists(imatch.group(1)): + prefix = os.path.split(filein)[0] + incfile = prefix + '/' + imatch.group(1) + else: + incfile = imatch.group(1) + read_spice_lib(incfile, celldefs, debug) + continue + + ematch = endsrex.match(line) + if ematch: + if insub: + insub = False + else: + print("Error: .ends outside of a subcircuit") + continue + + if not insub: + lmatch = subrex.match(line) + if lmatch: + subname = lmatch.group(1) + if subname in celldefs: + if debug: + print("Found subcircuit " + subname) + # Read information on spice port order into celldefs record + # Because line extensions have been handled, all information + # is on the first line. + cellrec = celldefs[subname] + cellrec['spicepins'] = lmatch.group(2).split() + + # Read to the ".ends" statement. + insub = True + +def write_models(cellsused, celldefs, ofile): + # Write the .model statement for all cells used + + imprex = re.compile('([10\)])[ \t]+([10\(])') + primerex = re.compile('([10\)])[ \t]*\'') + for cellname in cellsused: + cellrec = celldefs[cellname] + if len(cellrec['function']) > 0: + print('* ' + cellname + ' ' + cellrec['function'][0], file=ofile) + else: + print('* ' + cellname + ' (no function)', file=ofile) + if cellrec['type'] == 'flop': + continue + elif cellrec['type'] == 'latch': + continue + elif cellrec['nin'] == 0: + continue + else: + tabstr = '' + nout = cellrec['nout'] + nin = cellrec['nin'] + for k in range(0, nout): + # Handle tristate functions + # If triidx < nin then triidx, tripin, and tripos are all valid + if 'tristate' in cellrec: + trirec = cellrec['tristate'] + if trirec[0] == '~': + tripos = False + tripin = trirec[1:] + else: + tripos = True + tripin = trirec + for triidx in range(0, nin): + if cellrec['inputs'][triidx] == tripin: + break + else: + triidx = nin + + pstring = parse_pin(cellrec['function'][k]) + nvals = 2**nin + for i in range(0, nvals): + binstring = '{0:{fill}{width}b}'.format(i, fill='0', width=nin) + + psubs = pstring + for j in range(0, nin): + bchar = cellrec['inputs'][nin - j - 1] + bval = binstring[j] + 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) + + try: + tval = eval('(' + psubs + ')&1') + except (SyntaxError, NameError): + tabstr = '' + print("Could not evaluate function " + cellrec['function'][k]) + break + + if tval: + bitstr = '1' + else: + bitstr = '0' + + if triidx < nin: + if binstring[triidx] == '1' and tripos == True: + bitstr = 'Z' + elif binstring[triidx] == '0' and tripos == False: + bitstr = 'Z' + + tabstr += bitstr + + if tabstr != '': + if nout == 1: + # General n-input LUT model + print('.model d_lut_' + cellname + ' d_lut (rise_delay=1n fall_delay=1n input_load=10f', file=ofile) + print('+ table_values "' + tabstr + '")', file=ofile) + if debug: + print('Cell ' + cellname + ' has table ' + tabstr) + else: + # Even more general n-input, m-output LUT model + idelstr = ('2n ' * nin).strip() + ilodstr = ('1.0p ' * nin).strip() + odelstr = ('50n ' * nout).strip() + + print('.model d_genlut_' + cellname + ' d_genlut (', file=ofile) + print('+ rise_delay=[' + odelstr + ']', file=ofile) + print('+ fall_delay=[' + odelstr + ']', file=ofile) + print('+ input_load=[' + ilodstr + ']', file=ofile) + print('+ input_delay=[' + idelstr + ']', file=ofile) + print('+ table_values "' + tabstr + '")', file=ofile) + else: + print('No output for ' + cellname) + +def read_spice(filein, fileout, celldefs, debug, modelfile): + # Read a top-level SPICE file with a digital standard cell subcircuit, and + # write it back out as a subcircuit of XSPICE models and calls with + # appropriate A-to-D and D-to-A bridges. + + # If 'modelfile' is given, then do not write out model lines + # for the digital cells, but just include the modelfile at top. + + with open(filein, 'r') as ifile: + spitext = ifile.read() + + increx = re.compile('^[^\*]*[ \t]*.include[ \t]+([^ \t]+).*$', re.IGNORECASE) + subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+)(.*)$', re.IGNORECASE) + xrex = re.compile('^[ \t]*X([^ \t]+)(.*)$', re.IGNORECASE) + comrex = re.compile('^[\*]+.*$', re.IGNORECASE) + endsrex = re.compile('^[^\*]*[ \t]*.ends.*$', re.IGNORECASE) + + # Merge continuation lines in input + spilines = spitext.replace('\n+', ' ').splitlines() + if debug: + print("Reading spice file, " + str(len(spilines)) + " lines.") + + # Get expected name of subcircuit from filename + fileroot = os.path.split(filein)[1] + subname = os.path.splitext(fileroot)[0] + + cellsused = [] + + print("Writing xspice file") + with open(fileout, 'w') as ofile: + print("* XSpice netlist created from SPICE and liberty sources by spi2xspice.py", file=ofile) + echoout = False + lineno = 0 + for line in spilines: + lineno += 1 + + # Substitute for all characters "<" and ">" + line = line.replace('<', '_').replace('>', '_') + + # All comment lines go to the output + cmatch = comrex.match(line) + if cmatch: + print(line, file=ofile) + continue + + # If there is an include line, parse it and read the + # library cells from that include file. + imatch = increx.match(line) + if imatch: + read_spice_lib(imatch.group(1), celldefs, debug) + continue + + xmatch = xrex.match(line) + if xmatch: + # Replace subcircuit call with xspice call + instname = xmatch.group(1) + conns = xmatch.group(2).split() + pins = conns[0:-1] + cellname = conns[-1] + + if cellname not in celldefs: + print("Could not find cellname " + cellname + " from subcircuit line " + str(lineno)) + continue + + if cellname not in cellsused: + cellsused.append(cellname) + + cellrec = celldefs[cellname] + # Tricky! For each pin, find the corresponding subcircuit + # pin name. Then look up that pin name in the cell record + # and find if it is an input or an output. Then assign + # the pin direction to the connecting net. Then compile + # the list of input and output nets based on the XSPICE + # model's port order. + + if not 'spicepins' in cellrec: + print('Cell ' + cellname + ' does not have SPICE pin order defined!') + continue + + # Print the record. Special handling for flops and latches. + if cellrec['type'] == 'flop': + + dclk = 'NULL' + ddata = 'NULL' + dreset = 'NULL' + dset = 'NULL' + dq = 'NULL' + dqbar = 'NULL' + + if 'clock' in cellrec: + clkpin = cellrec['clock'] + if clkpin[0] == '~': + subpin = clkpin[1:] + else: + subpin = clkpin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if clkpin[0] == '~': + dclk = '~' + pins[i] + else: + dclk = pins[i] + + if 'data' in cellrec: + datapin = cellrec['data'] + if datapin[0] == '~': + subpin = datapin[1:] + else: + subpin = datapin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if datapin[0] == '~': + ddata = '~' + pins[i] + else: + ddata = pins[i] + + if 'set' in cellrec: + setpin = cellrec['set'] + if setpin[0] == '~': + subpin = setpin[1:] + else: + subpin = setpin + if debug: + print("flop setpin = " + setpin + " subpin = " + subpin) + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if setpin[0] == '~': + dset = '~' + pins[i] + else: + dset = pins[i] + + if 'reset' in cellrec: + resetpin = cellrec['reset'] + if resetpin[0] == '~': + subpin = resetpin[1:] + else: + subpin = resetpin + if debug: + print("flop resetpin = " + resetpin + " subpin = " + subpin) + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if resetpin[0] == '~': + dreset = '~' + pins[i] + else: + dreset = pins[i] + + for subpin in cellrec['outputs']: + subidx = cellrec['outputs'].index(subpin) + function = cellrec['function'][subidx] + if subpin in cellrec['spicepins']: + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'output' + if 'funcneg' in cellrec: + if cellrec['funcneg'] == function: + dqbar = pins[i] + if 'funcpos' in cellrec: + if cellrec['funcpos'] == function: + dq = pins[i] + + print('A' + instname + ' ' + ddata + ' ' + dclk + ' ' + dset + ' ' + dreset + ' ' + dq + ' ' + dqbar + ' ddflop', file=ofile) + + elif cellrec['type'] == 'latch': + + dena = 'NULL' + ddata = 'NULL' + dreset = 'NULL' + dset = 'NULL' + dq = 'NULL' + dqbar = 'NULL' + + if 'enable' in cellrec: + enapin = cellrec['enable'] + if enapin[0] == '~': + subpin = enapin[1:] + else: + subpin = enapin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if enapin[0] == '~': + dena = '~' + pins[i] + else: + dena = pins[i] + + if 'data' in cellrec: + datapin = cellrec['data'] + if datapin[0] == '~': + subpin = datapin[1:] + else: + subpin = datapin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if datapin[0] == '~': + ddata = '~' + pins[i] + else: + ddata = pins[i] + + if 'set' in cellrec: + setpin = cellrec['set'] + if setpin[0] == '~': + subpin = setpin[1:] + else: + subpin = setpin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if setpin[0] == '~': + dset = '~' + pins[i] + else: + dset = pins[i] + + if 'reset' in cellrec: + resetpin = cellrec['reset'] + if resetpin[0] == '~': + subpin = resetpin[1:] + else: + subpin = resetpin + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'input' + if resetpin[0] == '~': + dreset = '~' + pins[i] + else: + dreset = pins[i] + + for subpin in cellrec['outputs']: + subidx = cellrec['outputs'].index(subpin) + function = cellrec['function'][subidx] + if subpin in cellrec['spicepins']: + i = cellrec['spicepins'].index(subpin) + if pins[i] in pindefs: + pindefs[pins[i]] = 'output' + if 'funcneg' in cellrec: + if cellrec['funcneg'] == function: + dqbar = pins[i] + if 'funcpos' in cellrec: + if cellrec['funcpos'] == function: + dq = pins[i] + + print('A' + instname + ' ' + ddata + ' ' + dena + ' ' + dset + ' ' + dreset + ' ' + dq + ' ' + dqbar + ' dlatch', file=ofile) + + else: + inlist = [] + outlist = [] + for subpin in cellrec['inputs']: + if subpin in cellrec['spicepins']: + 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' + + for subpin in cellrec['outputs']: + if subpin in cellrec['spicepins']: + 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' + + intext = ' '.join(inlist) + outtext = ' '.join(outlist) + + if debug: + print("Instance = " + instname + "; inputs = " + intext + "; outputs = " + outtext) + + if len(inlist) == 0: + if cellrec['function'] == '1': + print('A' + instname + ' ' + outtext + ' done', file=ofile) + elif cellrec['function'] == '0': + print('A' + instname + ' ' + outtext + ' dzero', file=ofile) + else: + print("Cell " + cellname + " has no inputs and no constant function.") + elif len(outlist) == 1: + print('A' + instname + ' [' + intext + '] ' + outtext + ' d_lut_' + cellname, file=ofile) + else: + print('A' + instname + ' [' + intext + '] [' + outtext + '] d_genlut_' + cellname, file=ofile) + + continue + + lmatch = subrex.match(line) + if lmatch: + # To do: Maybe handle the difference between subcircuit and file name + # better? For now, just take the first subcircuit found and run with it. + if lmatch.group(1) != subname: + subname = lmatch.group(1) + + if lmatch.group(1) == subname: + echoout = False + if debug: + print("Found subcircuit " + subname) + # Prepend all signal names with "a_" + pins = lmatch.group(2).split() + pindefs = {} + # Use pindefs dict to track which pins are input and output + for pin in pins: + pindefs[pin] = 'unknown' + + # Write modified .subckt line + if debug: + print("Rewriting subcircuit " + subname) + print(".subckt " + subname, end='', file=ofile) + for pin in pins: + print(' a_' + pin, end='', file=ofile) + print('', file=ofile) + + else: + print('Unexpected subcircuit ' + lmatch.group(1) + ' found in netlist.') + echoout = True + + ematch = endsrex.match(line) + if ematch: + if not echoout: + # Write A-to-D and D-to-A bridge models + print("", file=ofile) + if modelfile != '': + print(".include " + modelfile, file=ofile) + else: + print(".model todig_3v adc_bridge(in_high=2.0 in_low=1.0 rise_delay=10n fall_delay=10n)", file=ofile) + print(".model toana_3v dac_bridge(out_high=3.0 out_low=0.0)", file=ofile) + print("", file=ofile) + + # Write d_dff, d_dlatch, d_pullup, and d_pulldown models + print(".model ddflop d_dff(ic=0 rise_delay=1n fall_delay=1n)", file=ofile) + print(".model dzero d_pulldown(load=1p)", file=ofile) + print(".model done d_pullup(load=1p)", file=ofile) + print("", file=ofile) + + # Write A-to-D and D-to-A bridges + inum = 0 + onum = 0 + for pinname in pindefs: + if pindefs[pinname] == 'input': + inum += 1 + print("AA2D" + str(inum) + " [a_" + pinname + "] [" + pinname + "] todig_3v", file=ofile) + elif pindefs[pinname] == 'output': + onum += 1 + print("AD2A" + str(onum) + " [" + pinname + "] [a_" + pinname + "] toana_3v", file=ofile) + print("", file=ofile) + + echoout = True + + if echoout: + print(line, file=ofile) + + # At the end, write all of the LUT-based digital models. + if modelfile == '': + print("", file=ofile) + write_models(cellsused, celldefs, ofile) + + print(".end", file=ofile) + +def write_lib(fileout, celldefs, debug): + # Write a library file containing the models of all of the cells in the + # liberty file, which can then be included in other xspice simulatable + # netlists. + + print("Writing xspice model library") + with open(fileout, 'w') as ofile: + print("* XSpice library created from liberty sources by spi2xspice.py", file=ofile) + echoout = False + + print("", file=ofile) + print(".model todig_3v adc_bridge(in_high=2.0 in_low=1.0 rise_delay=10n fall_delay=10n)", file=ofile) + print(".model toana_3v dac_bridge(out_high=3.0 out_low=0.0)", file=ofile) + print("", file=ofile) + + # Write d_dff, d_dlatch, d_pullup, and d_pulldown models + print(".model ddflop d_dff(ic=0 rise_delay=1n fall_delay=1n)", file=ofile) + print(".model dzero d_pulldown(load=1p)", file=ofile) + print(".model done d_pullup(load=1p)", file=ofile) + + cellsused = [] + for cell in celldefs: + cellsused.append(cell) + + # At the end, write all of the LUT-based digital models. + print("", file=ofile) + write_models(cellsused, celldefs, ofile) + print(".end", file=ofile) + +def parse_pin(function): + # Handle n' as way of expressing ~n or !n + primerex = re.compile('([^ \t]+)[ \t]*\'') + outparenrex = re.compile('^[ \t]*\([ \t]*(.+)[ \t]*\)[ \t]*$') + parenrex = re.compile('\([ \t]*([^ \t\)|&~^]+)[ \t]*\)') + pstring = function.strip('"').strip() + pstring = pstring.replace('*', '&').replace('+', '|').replace('!', '~') + pstring = outparenrex.sub('\g<1>', pstring) + pstring = parenrex.sub('\g<1>', pstring) + pstring = primerex.sub('~\g<1>', pstring) + return pstring + +def read_liberty(filein, debug): + celldefs = {} + cellrex = re.compile('[ \t]*cell[ \t]*\(([^)]+)\)') + pinrex = re.compile('[ \t]*pin[ \t]*\(([^)]+)\)') + lat1rex = re.compile('[ \t]*latch[ \t]*\(([^)]+)\)') + lat2rex = re.compile('[ \t]*latch[ \t]*\(([^, \t]+)[ \t]*,[ \t]*([^),]+)\)') + ff1rex = re.compile('[ \t]*ff[ \t]*\(([^)]+)\)') + ff2rex = re.compile('[ \t]*ff[ \t]*\(([^, \t]+)[ \t]*,[ \t]*([^),]+)\)') + staterex = re.compile('[ \t]*next_state[ \t]*:[ \t]*([^;]+);') + clockrex = re.compile('[ \t]*clocked_on[ \t]*:[ \t]*([^;]+);') + setrex = re.compile('[ \t]*preset[ \t]*:[ \t]*([^;]+);') + resetrex = re.compile('[ \t]*clear[ \t]*:[ \t]*([^;]+);') + datarex = re.compile('[ \t]*data_in[ \t]*:[ \t]*([^;]+);') + enarex = re.compile('[ \t]*enable[ \t]*:[ \t]*([^;]+);') + trirex = re.compile('[ \t]*three_state[ \t]*:[ \t]*([^;]+);') + funcrex = re.compile('[ \t]*function[ \t]*:[ \t]*\"?[ \t]*([^"]+)[ \t]*\"?') + with open(filein, 'r') as ifile: + lines = ifile.readlines() + if debug: + print("Reading liberty file, " + str(len(lines)) + " lines.") + for line in lines: + lmatch = cellrex.match(line) + if lmatch: + cellname = lmatch.group(1) + if debug: + print("Found cell " + cellname) + cellrec = {} + cellrec['inputs'] = [] + cellrec['outputs'] = [] + cellrec['nin'] = 0 + cellrec['nout'] = 0 + cellrec['function'] = [] + # NOTE: average rise and fall times need to be + # averaged from the data, to get a general relation + # between timing and drive strength. + cellrec['rise'] = 1.0 + cellrec['fall'] = 1.0 + cellrec['type'] = 'comb' + celldefs[cellname] = cellrec + continue + + pmatch = pinrex.match(line) + if pmatch: + pinname = pmatch.group(1) + if debug: + print("Found input pin " + pinname) + cellrec['inputs'].append(pinname) + cellrec['nin'] += 1 + continue + + lmatch = lat2rex.match(line) + if lmatch: + if debug: + print("Found latch"); + cellrec['type'] = 'latch' + cellrec['funcpos'] = lmatch.group(1) + cellrec['funcneg'] = lmatch.group(2) + continue + + lmatch = lat2rex.match(line) + if lmatch: + if debug: + print("Found latch"); + cellrec['type'] = 'latch' + cellrec['funcpos'] = lmatch.group(1) + continue + + rmatch = ff2rex.match(line) + if rmatch: + if debug: + print("Found flop"); + cellrec['type'] = 'flop' + cellrec['funcpos'] = rmatch.group(1) + cellrec['funcneg'] = rmatch.group(2) + continue + + rmatch = ff1rex.match(line) + if rmatch: + if debug: + print("Found flop"); + cellrec['type'] = 'flop' + cellrec['funcpos'] = rmatch.group(1) + continue + + fmatch = funcrex.match(line) + if fmatch: + function = fmatch.group(1) + if debug: + print("Found function " + function + " and output pin " + pinname) + # If pin has a function, it's an output, not an input, + # so add it to the outputs list and remove it from the + # inputs list. + cellrec['outputs'].append(pinname) + cellrec['nout'] += 1 + cellrec['inputs'].remove(pinname) + cellrec['nin'] -= 1 + cellrec['function'].append(function) + continue + + smatch = staterex.match(line) + if smatch: + if debug: + print('Found data input') + cellrec['data'] = parse_pin(smatch.group(1)) + continue + + cmatch = clockrex.match(line) + if cmatch: + if debug: + print('Found clock input') + cellrec['clock'] = parse_pin(cmatch.group(1)) + continue + + smatch = setrex.match(line) + if smatch: + if debug: + print('Found set input ' + smatch.group(1)) + cellrec['set'] = parse_pin(smatch.group(1)) + continue + + rmatch = resetrex.match(line) + if rmatch: + if debug: + print('Found reset input ' + rmatch.group(1)) + cellrec['reset'] = parse_pin(rmatch.group(1)) + continue + + dmatch = datarex.match(line) + if dmatch: + if debug: + print('Found data input') + cellrec['data'] = parse_pin(dmatch.group(1)) + continue + + ematch = enarex.match(line) + if ematch: + if debug: + print('Found enable input') + cellrec['enable'] = parse_pin(ematch.group(1)) + continue + + tmatch = trirex.match(line) + if tmatch: + if debug: + print('Found tristate output') + cellrec['tristate'] = parse_pin(tmatch.group(1)) + continue + + return celldefs + + +if __name__ == '__main__': + + options = [] + arguments = [] + for item in sys.argv[1:]: + if item.find('-', 0) == 0: + options.append(item) + else: + arguments.append(item) + + if '-debug' in options: + debug = True + else: + debug = False + + if len(arguments) >= 3: + print("Reading liberty netlist " + arguments[0]) + print("Reading spice netlist " + arguments[1]) + print("Writing xspice netlist " + arguments[2]) + celldefs = read_liberty(arguments[0], debug) + if len(arguments) >= 4: + modelfile = arguments[3] + else: + modelfile = '' + read_spice(arguments[1], arguments[2], celldefs, debug, modelfile) + print("Done.") + elif len(arguments) == 2: + # Library-only option + print("Reading liberty netlist " + arguments[0]) + print("Writing xspice model library " + arguments[1]) + celldefs = read_liberty(arguments[0], debug) + write_lib(arguments[1], celldefs, debug) + print("Done.") + else: + print("Usage:") + print("spi2xspice.py <liberty file> <input spice> <output spice> [<xspice lib>]") + print("spi2xspice.py <liberty file> <output xspice lib>") + diff --git a/scripts/synthesize.sh b/scripts/synthesize.sh index c5f2808..1ef371e 100755 --- a/scripts/synthesize.sh +++ b/scripts/synthesize.sh @@ -104,23 +104,30 @@ while ($yerrcnt > 1) # works in yosys 0.3.1 and newer, the following line works for the # purpose of querying the hierarchy in all versions. -if ( !( -f ${rootname}.v )) then - echo "Error: Verilog source file ${rootname}.v cannot be found!" \ +set vext = "v" +set svopt = "" + +if ( !( -f ${rootname}.${vext} )) then + set vext = "sv" + set svopt = "-sv" + if ( !( -f ${rootname}.${vext} )) then + echo "Error: Verilog source file ${rootname}.v (or .sv) cannot be found!" \ |& tee -a ${synthlog} + endif endif cat > ${rootname}.ys << EOF # Synthesis script for yosys created by qflow read_liberty -lib -ignore_miss_dir -setattr blackbox ${libertypath} -read_verilog ${rootname}.v +read_verilog ${svopt} ${rootname}.${vext} EOF foreach subname ( $uniquedeplist ) - if ( !( -f ${subname}.v )) then - echo "Error: Verilog source file ${subname}.v cannot be found!" \ - |& tee -a ${synthlog} + if ( !( -f ${subname}.${vext} )) then + echo "Error: Verilog source file ${subname}.${vext} cannot be found!" \ + |& tee -a ${synthlog} endif - echo "read_verilog ${subname}.v" >> ${rootname}.ys + echo "read_verilog ${svopt} ${subname}.${vext}" >> ${rootname}.ys end cat >> ${rootname}.ys << EOF @@ -201,11 +208,11 @@ endif cat > ${rootname}.ys << EOF read_liberty -lib -ignore_miss_dir -setattr blackbox ${libertypath} -read_verilog ${rootname}.v +read_verilog ${svopt} ${rootname}.${vext} EOF foreach subname ( $uniquedeplist ) - echo "read_verilog ${subname}.v" >> ${rootname}.ys + echo "read_verilog ${svopt} ${subname}.${vext}" >> ${rootname}.ys end # Will not support yosys 0.0.x syntax; flag a warning instead @@ -323,6 +330,7 @@ cat >> ${rootname}.ys << EOF # Cleanup opt clean +rename -enumerate write_blif ${blif_opts} ${rootname}_mapped.blif EOF @@ -531,17 +539,8 @@ ${bindir}/blif2Verilog -c -v ${vddnet} -g ${gndnet} ${rootname}.blif \ ${bindir}/blif2Verilog -c -p -v ${vddnet} -g ${gndnet} ${rootname}.blif \ > ${rootname}.rtlnopwr.v -echo "Running blif2BSpice." |& tee -a ${synthlog} -if ("x${spicefile}" == "x") then - set spiceopt="" -else - set spiceopt="-l ${spicepath}" -endif -${bindir}/blif2BSpice -p ${vddnet} -g ${gndnet} ${spiceopt} \ - ${rootname}.blif > ${rootname}.spc - #--------------------------------------------------------------------- -# Spot check: Did blif2Verilog or blif2BSpice exit with an error? +# Spot check: Did blif2Verilog 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. #--------------------------------------------------------------------- @@ -558,29 +557,44 @@ if ( !( -f ${rootname}.rtlnopwr.v || \ |& tee -a ${synthlog} endif +#--------------------------------------------------------------------- + +echo "Running blif2BSpice." |& tee -a ${synthlog} +if ("x${spicefile}" == "x") then + set spiceopt="" +else + set spiceopt="-l ${spicepath}" +endif +${bindir}/blif2BSpice -i -p ${vddnet} -g ${gndnet} ${spiceopt} \ + ${rootname}.blif > ${rootname}.spc + +#--------------------------------------------------------------------- +# Spot check: Did blif2BSpice 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 ${rootname}.spc || \ ( -M ${rootname}.spc < -M ${rootname}.blif ))) then echo "blif2BSpice failure: No file ${rootname}.spc created." \ |& tee -a ${synthlog} - echo "Premature exit." |& tee -a ${synthlog} - echo "Synthesis flow stopped due to error condition." >> ${synthlog} - exit 1 -endif +else + echo "Running spi2xspice.py" |& tee -a ${synthlog} + if ("x${spicefile}" == "x") then + set spiceopt="" + else + set spiceopt="-l ${spicepath}" + endif + ${scriptdir}/spi2xspice.py ${libertypath} ${rootname}.spc \ + ${rootname}.xspice +endif -#--------------------------------------------------------------------- -# Spot check: Did blif2cel produce file ${rootname}.cel? -#--------------------------------------------------------------------- -# -# if ( !( -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" -# echo "Premature exit." |& tee -a ${synthlog} -# echo "Synthesis flow stopped due to error condition." >> ${synthlog} -# exit 1 -# endif +if ( !( -f ${rootname}.xspice || \ + ( -M ${rootname}.xspice < -M ${rootname}.spc ))) then + echo "spi2xspice.py failure: No file ${rootname}.xspice created." \ + |& tee -a ${synthlog} +endif #--------------------------------------------------------------------- |