summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorRuben Undheim <ruben.undheim@gmail.com>2017-07-13 22:25:58 +0200
committerRuben Undheim <ruben.undheim@gmail.com>2017-07-13 22:25:58 +0200
commit6bfa88a0ac7a4f8d69b344edd1795404e9048d20 (patch)
tree6ffa0b15851a01e9ab4ea8abf83001e769c773ef /scripts
parent548aa8a30317aade2d08d55499cf8860513bd081 (diff)
New upstream version 1.1.58
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile109
-rw-r--r--scripts/Makefile.in10
-rwxr-xr-xscripts/addspacers.tcl.in1414
-rwxr-xr-xscripts/placement.sh70
-rw-r--r--scripts/qflow.sh.in17
-rwxr-xr-xscripts/router.sh4
-rwxr-xr-xscripts/spi2xspice.py762
-rwxr-xr-xscripts/synthesize.sh86
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
#---------------------------------------------------------------------