#!/bin/sh # The next line restarts using expectk \ exec expectk "$0" "$@" #*********** # The comments with the code here are meant to address the details of the # code itself, not the functionality of XsimView. Refer to the XsimView's # man page for a discussion of functionality. # # The overall algorithm of XsimView is described at the bottom of the code # below where the "main" of XsimView is given. The anciliary procedures # are desribed in turn below. #----------- #********** # source fileSelect.src # # why: fileSelect provides the ability to save files. Here, it # is used to allow the user to select a filename so that in.simv, # the temporary file used for output, can be saved to whatever # name they desire. # All from fileSelect.src # Begins the file loading process. Can take one argument, fileName # which would specify the file being loaded, in which case, # fileSelect process is skipped and the file is directly loaded. proc loadFile {{file {}} {type {}}} { global globals set globals(optList) {} # if file is empty, ask user for new file, if still empty, exit # since they must have hit cancel or something else went wrong if {$file == {}} {set file [fileSelect Load]} if {$file == {}} {return} # if type is nil, then # find the final letters after last . to get file type if {$type == {}} { set type [string range [file extension $file] 1 end] } if {$globals(options)} { switch -- $type { mv {set globals(optList) \ [optionRead $globals(read_blif_mvOptions)]} blif {set globals(optList) \ [optionRead $globals(read_blifOptions)]} vl {set globals(optList) \ [optionRead $globals(read_verilogOptions)]} fair {set globals(optList) \ [optionRead $globals(read_fairnessOptions)]} ctl {set globals(optList) \ [optionRead $globals(model_checkOptions)]} } } switch -- $type { mv {Send "rlmv $globals(optList) $file"} blif {Send "read_blif $file"} vl {Send "read_verilog $file"} fair {Send "read_fairness $file"} ctl {Send "model_check $file"} default {loadFile "$file [giveFileType $file]"} } } proc writeFile {{file {}} {type {}}} { global globals set globals(optList) {} # if file is empty, ask user for filename to write to. # If still empty, then they must have hit cancel or something if {$file == {}} {set file [fileSelect Write]} if {$file == {}} {return} # find the final letters after last . to get file type set type [string range [file extension $file] 1 end] if {$globals(options)} { switch -- $type { mv {set globals(optList) \ [optionRead $globals(write_blif_mvOptions)]} blif {set globals(optList) \ [optionRead $globals(austin_write_blifOptions)]} } } switch -- $type { mv {Send "write_blif_mv $globals(optList) $file"} blif {Send "austin_write_blif $globals(optList) $file"} default {writeFile "$file.[giveFileType $file]"} } } proc giveFileType {file} { global temp globals toplevel .q set gFTDone 0 wm title .q "What type of file?" set short [string range $file [expr [string last / $file] + 1] end] message .q.msg -text "What type is $short ?" pack .q.msg -side top bind .q {set gFTDone 1} # Set up radio buttons for type of file to load frame .q.radio foreach type {{blif blif} {blif_mv mv} {verilog vl} {fairness fair}} { radiobutton .q.radio.[lindex $type 1] -variable temp \ -text [lindex $type 0] -value [lindex $type 1] pack .q.radio.[lindex $type 1] -side top } pack .q.radio -side top # Create the OK button # The OK button has a rim to indicate it is the default frame .q.button -bd 10 frame .q.button.ok -bd 2 -relief sunken button .q.button.ok.b -text OK -command {set gFTDone 1} pack .q.button.ok.b pack .q.button.ok -side top pack .q.button -side top tkwait variable gFTDone destroy .q return $temp } proc fileSelect {why {default {}}} { global fileSelect globals set globals(options) 0 set fileSelect(why) $why catch {destroy .fileSelect} set t [toplevel .fileSelect -bd 4] wm title $t $why message $t.msg -aspect 1000 -text $why pack $t.msg -side top -fill x # Create a read_only entry for the current pwd set fileSelect(dirEnt) [entry $t.dir -width 15 \ -relief flat -state disabled] pack $t.dir -side top -fill x # Create an entry for the pathname # The value is kept in the fileSelect(path) frame $t.top label $t.top.l -text "File:" -padx 0 set e [entry $t.top.path -relief sunken -textvariable fileSelect(path)] pack $t.top -side top -fill x pack $t.top.l -side left pack $t.top.path -side right -fill x -expand true set fileSelect(pathEnt) $e # Use $e to set up bindings for entry widget bind $e fileSelectOK bind $e fileSelectCancel bind $e fileSelectComplete focus $e # Create a listbox to hold the directory contents listbox $t.list -yscrollcommand [list $t.scroll set] scrollbar $t.scroll -command [list $t.list yview] # A single click copies the name into the entry # A double-click selects the name bind $t.list {fileSelectClick %y} bind $t.list { fileSelectClick %y; fileSelectOK } # Create the OK and cancel buttons and options button # The OK button has a rim to indicate it is the default frame $t.buttons -bd 10 frame $t.buttons.ok -bd 2 -relief sunken button $t.buttons.ok.b -text OK -command fileSelectOK button $t.buttons.cancel -text Cancel -command fileSelectCancel checkbutton $t.buttons.options -text "Choose Options" \ -variable globals(options) # Pack the list, scrollbar and button box # in a horizontal stack below the upper widgets pack $t.list -side left -fill both -expand true pack $t.scroll -side left -fill both pack $t.buttons -side left -fill both pack $t.buttons.ok $t.buttons.cancel $t.buttons.options -side top \ -padx 10 -pady 5 pack $t.buttons.ok.b -padx 4 -pady 4 # Initialize variables in the directory set fileSelect(path) {} set dir [pwd] set fileSelect(dir) {} set fileSelect(done) 0 # Wait for the listbox to be visible so we # can provide feedback during the listing tkwait visibility .fileSelect.list fileSelectList $dir tkwait variable fileSelect(done) destroy .fileSelect return $fileSelect(path) } # Creates the list of dirs and files to put into the listbox, if not given proc fileSelectList { dir {files {}} } { global fileSelect # Update the directory set e $fileSelect(dirEnt) $e config -state normal $e delete 0 end $e insert 0 $dir $e config -state disabled # Give the user some feedback set fileSelect(dir) $dir .fileSelect.list delete 0 end .fileSelect.list insert 0 Listing... .fileSelect.list delete 0 # If files is undefined, then match all files in current dir if {[string length $files] == 0} { # List the directory and add an entry for the parent dir. set files [glob -nocomplain $fileSelect(dir)/*] .fileSelect.list insert end ../ } # Take files, and sort all dirs to front set dirs {} set others {} foreach f [lsort $files] { if [file isdirectory $f] { lappend dirs [file tail $f]/ } else { lappend others [file tail $f] } } # Insert all dirs and files into the listbox foreach f [concat $dirs $others] { .fileSelect.list insert end $f } } # Checks files specified, and whether or not to return a value, or # make a new listbox. Basically, first check if command is to go to # parent directory. If not, check if fileSelect(path) is a directory. # If so, create new list. If not, check if is a file. If so, return # the file, else, try some magic to go somewhere else. (also known # as file name completion. proc fileSelectOK {} { global fileSelect # Handle the special case of the parent directory if {[regexp {\.\./} $fileSelect(path)]} { set fileSelect(path) {} fileSelectList [file dirname $fileSelect(dir)] return } # create a full path from fileSelect(dir) and fileSelect(path) set path $fileSelect(dir)/$fileSelect(path) if [file isdirectory $path] { set fileSelect(path) {} fileSelectList $path return } if [file exists $path] { set fileSelect(path) $path set fileSelect(done) 1 return } # Neither a file nor a directory # See if a glob will find something # catch returns true if something goes wrong if [catch {glob $path} files] { # Okay, maybe they typed in a new absolute pathname and we # should try fileSelect(path) as the ENTIRE path name if [catch {glob $fileSelect(path)} path] { # Okay, nothing there, if we're writing, then accept as # as new filename, else try filename completion. if {[string match $fileSelect(why) Write]} { set fileSelect(done) 1 return } else { # Try file name completion fileSelectComplete return } } else { # OK - there are files there, so let's reassign the dir and path set fileSelect(dir) [file dirname $fileSelect(path)] set fileSelect(path) [file tail $fileSelect(path)] fileSelectOK return } } else { # Current directory has stuff in it, catch returned no error # Ok - current directory is ok, select file or list 'em. if {[llength [split $files]] == 1} { set fileSelect(path) $files fileSelectOK } else { set fileSelect(dir) [file dirname [lindex $files 0]] fileSelectList $fileSelect(dir) $files } } } # Quits out of fileSelection and moves on. proc fileSelectCancel {} { global fileSelect set fileSelect(done) 1 set fileSelect(path) {} } # Find the file the user clicked on. proc fileSelectClick { y } { # Take the item the user clicked on global fileSelect set l .fileSelect.list set fileSelect(path) [$l get [$l nearest $y]] focus $fileSelect(pathEnt) } # Big nasty routine to do file name completion proc fileSelectComplete {} { global fileSelect # Do name completion, Nuke something. set fileSelect(path) [string trim $fileSelect(path) \t\ ] # Figure out what directory we are looking at, # dir is directory, tail is partial name if {[string match /* $fileSelect(path)]} { set dir [file dirname $fileSelect(path)] set tail [file tail $fileSelect(path)] } elseif [string match ~* $fileSelect(path)] { if [catch {file dirname $fileSelect(path)} dir] { return; # Bad user } set tail [file tail $fileSelect(path)] } else { set path $fileSelect(dir)/$fileSelect(path) set dir [file dirname $path] set tail [file tail $path] } # See what files are there set files [glob -nocomplain $dir/$tail*] if {[llength [split $files]] == 1} { # Matched a single file set fileSelect(dir) $dir set fileSelect(path) [file tail $files] } else { if {[llength [split $files]] > 1} { # Find the longest common prefix set l [expr [string length $tail]-1] set miss 0 # remember that files has absolute paths set file1 [file tail [lindex $files 0]] while {$miss == 0} { incr l if {$l == [string length $file1]} { # file1 is a prefix of all others break } set new [string range $file1 0 $l] foreach f $files { if ![string match $new* [file tail $f]] { set miss 1 incr l -1 break } } } set fileSelect(path) [string range $file1 0 $l] } fileSelectList $dir $files } } #********** # procedure match # # input: string is a string of characters # file is a string of characters that holds a file name. # # function: open the file given by input "file" and iteratively look for # a text line in "file" that a) does not start with a "#", and # b) whose first word (text string) matches input "string". Upon # finding such a line that meets this criteria, procedure match # returns a 1. If EOF is reached before a line meeting the said # criteria is found, then procedure match returns a 0. # proc match {string file} { set f [open $file r] while {1} { set cnt [gets $f inline] if {$cnt == -1} { close $f return 0 } set y [split $inline {}] set y1 [lindex $y 0] if {$y1 != "#"} { if {$string == $inline} { close $f return 1 } } } } #******************** # procedure parse # # input sf string that holds name of sim output file. # # output none # # function described with the code below. proc parse {sf} { # the following are globals. # observe is to hold all of the variables eligible for display. # ninps, nlats, nouts are to hold the number of inputs, latches, outputs. # cy is to hold the variable values for each cycle. # ncy is to hold the number of cycles # what is to hold the identity of each variable (input, latch, output). global observe ninps nlats nouts global cy ncy global what # the following globals are the display preference variables. # see the man page for a discussion of these variables. global bgtop bg1 bg2 global fgtop fg1 fg2 global fonttop font1 font2 global hlinescolor vlinescolor global cybarbg cybarfg # open the sim output file for parsing. set f [open $sf r] # find the .inputs, .latches, .outputs, and .initial lines in the sim output file # and make lists out of them. set finps 0 set flats 0 set fouts 0 set finit 0 while {$finps == 0 || $flats == 0 || $fouts == 0 || $finit == 0} { set cnt [gets $f inline] if {$cnt == -1} {break} split $inline set first [lindex $inline 0] switch $first { ".inputs" { set inps [lreplace $inline 0 0] set ninps [llength $inps] set finps 1 for {set j 0} {$j < $ninps} {incr j} { lappend what (I) } } ".latches" { set lats [lreplace $inline 0 0] set nlats [llength $lats] set flats 1 for {set j 0} {$j < $nlats} {incr j} { lappend what (L) } } ".outputs" { set outs [lreplace $inline 0 0] set nouts [llength $outs] set fouts 1 for {set j 0} {$j < $nouts} {incr j} { lappend what (O) } } ".initial" { set finit 1 } } } # if any of the .inputs, .latches, .outputs, or .initial lines are not # found, exit with an Error. if {$finps == 0} { puts "ERROR: No .inputs line was found!" return -1 } if {$flats == 0} { puts "ERROR: No .latches line was found!" return -1 } if {$fouts == 0} { puts "ERROR: No .outputs line was found!" return -1 } if {$finit == 0} { puts "ERROR: No .initial line was found!" return -1 } # list observe holds all the variables eligible to be viewed. # here the list of variables is dumped to the strike file (strk.simv) # so user can later access and select variables to strike. set observe [concat $inps $lats $outs] set ff [open strk.simv w] foreach i $observe { puts $ff $i } close $ff # Set defaults for the display preferences. # set bgtop Grey set bg1 White set bg2 Black set fgtop Black set fg1 Black set fg2 White set fonttop -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 set font1 -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 set font2 -adobe-courier-bold-r-normal--12-120-75-75-m-70-iso8859-1 set hlinescolor White set vlinescolor Yellow set cybarbg Blue set cybarfg White # And put them in the preferences file. # set ff [open prefs.simv w] puts $ff "bgtop $bgtop" puts $ff "bg1 $bg1" puts $ff "bg2 $bg2" puts $ff "fgtop $fgtop" puts $ff "fg1 $fg1" puts $ff "fg2 $fg2" puts $ff "fonttop $fonttop" puts $ff "font1 $font1" puts $ff "font2 $font2" puts $ff "hlinescolor $hlinescolor" puts $ff "vlinescolor $vlinescolor" puts $ff "cybarbg $cybarbg" puts $ff "cybarfg $cybarfg" close $ff # the following lines of code look for the beginning of the portion of # the sim output file that contains the cycle information and upon # finding it stores it in "array" for further processing. set i 0 while {1} { set cnt [gets $f inline] if {$cnt == -1} {break} set y [split $inline {}] set y1 [lindex $y 0] if {$y1 == {} || $y1 == "#" || $y1 == "."} {continue} set array($i) $inline incr i 1 } set ncy $i close $f # if no cycle lines were found, exit with an Error. if {$cnt == -1 && $i == 0} { puts "ERROR: No simulation results found!" return -1 } # the following lines of code parse the contents of "array" into # "cy". each list in "array" is itself a list of strings, this step turns # the cycle information so that each element in "cy" is a simple list of # strings. for {set j 0} {$j < $i} {incr j} { set z [split $array($j) \;] set p [lindex $z 0] set q [lindex $z 1] set r [lindex $z 2] set cy($j) [concat $p $q $r] } } #************* # procedure strike # # inputs sf string that holds file name # # outputs none # # function Reads the contents of the file pointed to by input "sf" and # based on its contents generates an array of flags that indicate # whether a given variable should be displayed. The array of # flags is in one-to-one correspondence with the array of variables # that are eligible to be displayed. proc strike {} { global observe global show nshow set nshow 0 set nobvs [llength $observe] set j 0 for {set i 0} {$i < $nobvs} {incr i} { set m [match [lindex $observe $i] strk.simv] if {$m == 0} { lappend show 0 } else { lappend show 1 incr nshow } } } #************* # procedure readprefs # # inputs pf string that holds file name # # outputs none # # function Reads the contents of the file pointed to by input "pf" and # sets the values of the preferences accordingly. See the "man" # page for description of the preferences currently supported. # proc readprefs {} { global bgtop bg1 bg2 global fgtop fg1 fg2 global fonttop font1 font2 global hlinescolor vlinescolor global cybarbg cybarfg global run set bgtop 0 set bg1 0 set bg2 0 set fgtop 0 set fg1 0 set fg2 0 set fonttop 0 set font1 0 set font2 0 set hlinescolor 0 set vlinescolor 0 set cybarbg 0 set cybarfg 0 set f [open prefs.simv r] while {1} { set cnt [gets $f inline] if {$cnt == -1} {break} set first [lindex $inline 0] switch $first { bgtop { set bgtop [lindex $inline 1] } bg1 { set bg1 [lindex $inline 1] } bg2 { set bg2 [lindex $inline 1] } fgtop { set fgtop [lindex $inline 1] } fg1 { set fg1 [lindex $inline 1] } fg2 { set fg2 [lindex $inline 1] } fonttop { set fonttop [lindex $inline 1] } font1 { set font1 [lindex $inline 1] } font2 { set font2 [lindex $inline 1] } hlinescolor { set hlinescolor [lindex $inline 1] } vlinescolor { set vlinescolor [lindex $inline 1] } cybarbg { set cybarbg [lindex $inline 1] } cybarfg { set cybarfg [lindex $inline 1] } } } close $f } #**************** # procedure findlongestvar # # input none # # output none # # function This procedure operates on the parsed lists of variables names and # values for each cycle to find a) the longest variable name, and b) # the longest value of any given variable. These values are then used # to set the corresponding field widths in the display. # proc findlongestvar {} { global observe global show global cywidth varwidth global cy ncy set nobvs [llength $observe] set maxlen 0 for {set i 0} {$i < $nobvs} {incr i} { set active [lindex $show $i] if {$active == 1} { set str [lindex $observe $i] set len [string length $str] if {$len > $maxlen} {set maxlen $len} } } set varwidth [expr ($maxlen+2)*0.25] set maxlen 0 for {set i 0} {$i < $ncy} {incr i} { for {set j 0} {$j < $nobvs} {incr j} { set active [lindex $show $j] if {$active == 1} { set str [lindex $cy($i) $j] set len [string length $str] if {$len > $maxlen} {set maxlen $len} } } } # Takes care of finding the maximum width for cycle width set cywidth_cycleInfo [expr (log($ncy)+2)*0.25] set cywidth_dataInfo [expr ($maxlen+2)*0.25] if {$cywidth_cycleInfo > $cywidth_dataInfo} { set cywidth $cywidth_cycleInfo } else { set cywidth $cywidth_dataInfo } } #*************** # procedure drawcanvas # # input none # # output none # # function This procedure carries out the actual displaying. It is described alongside # with the code below. # proc drawcanvas {} { #--------- # these globals are the display preferences, they are read # from file dprefs by procedure readprefs: # # varwidth holds the width of the left-most column that # lists the variables under observation # cywidth holds the width of the cycle columns that # lists the value of the variables for each # cycle. # global varwidth global cywidth #--------- # the following globals hold the items to be # displayed: # # cy is the array of lists, each element is a list # of the value of the items for that cycle, # ncy is the number of elements in cy, # show is an array that marks those elements in cy and # observe that are to be displayed, (i.e. after # some of the elements in observe are striken based # on the contents of the strike file. # nshow is the number of "1" elements in show, # observe is a list of _all_ the items that _can_ be displayed. # what is an array that identifies a variable as either an input, latch, or output. # global cy ncy global show nshow global observe global what #------------- # the following globals are the prefrences variables. # global bgtop bg1 bg2 global fgtop fg1 fg2 global fonttop font1 font2 global hlinescolor vlinescolor global cybarbg cybarfg #------------ # here the top level widget that holds the results display is # created and the two canvases that will be used to construct # the display are defined. # this width and height is a good start for a workstation monitor, # of course, the window can be resized in the usual way. # set wwidth 800 set wheight 500 global infile global t set t .top toplevel $t wm geometry $t ${wwidth}x${wheight} wm title $t "XsimView 0.1" set c1 $t.canvas1 set c2 $t.canvas2 # the top of the display holds generic information. # text $t.text -font $fonttop -bg $bgtop -fg $fgtop text $t.text1 -font $fonttop -bg $bgtop -fg $fgtop $t.text insert 1.0 "XSimView Rev. 0.1 (April 1996)\n" $t.text insert 2.0 "by J.S. Duran\n" $t.text insert 3.0 "ECE Department\n" $t.text insert 4.0 "UT Austin\n" $t.text1 insert 1.0 "Displaying file: $infile" place $t.text -x 0 -y 0 -relwidth 1.0 place $t.text1 -relx 0.5 -y 0 #--------------- # build the higher level canvas (holds the variable names at the left-most # of the display and also holds the lower level canvas). # set c1w [expr $wwidth] set c1h [expr $nshow+1] set c1dims [list 0 0 ${c1w} ${c1h}c] canvas $c1 -relief sunken -borderwidth 2 -scrollregion $c1dims -yscrollcommand "$t.vscroll set" -bg ${bg1} scrollbar $t.vscroll -command "$c1 yview" place $t.vscroll -relx 0.98 -rely 0.15 -relheight 0.85 -width 0.5c place $c1 -x 0 -rely 0.15 -relheight 0.85 -relwidth 1.0 set f $c1.frame frame $f -relief sunken -borderwidth 2 $c1 create window ${varwidth}c 0 -anchor nw -height ${c1h}c -width ${wwidth} -window $f #------------ # build the lower level canvas (holds the per cycle variable value information). # set c2xlen [expr $ncy*$cywidth*1.25] set c2scrl [list 0 0 ${c2xlen}c ${c1h}c] set hsw [expr $wwidth-$varwidth*40] canvas $f.c2 -relief sunken -borderwidth 2 -scrollregion $c2scrl -xscrollcommand "$t.hscroll set" -bg ${bg2} place $f.c2 -x 0 -y 0 -relheight 1.0 -relwidth 1.0 scrollbar $t.hscroll -orient horiz -command "$f.c2 xview" place $t.hscroll -x 0 -rely 0.125 -height 0.5c -relwidth 1.0 #------------ # place the variable names on the higher level canvas. also draw the horizontal grid # lines in the lower level canvas. # set q [llength $show] set v 1 for {set i 0} {$i < $q} {incr i} { set sh [lindex $show $i] if {$sh == 1} { set item1 [lindex $observe $i] set item2 [lindex $what $i] set item $item1$item2 $c1 create text ${varwidth}c ${v}c -text $item -anchor ne -fill $fg1 -font $font1 $f.c2 create line 0 [expr $v+0.4]c ${c2xlen}c [expr $v+0.4]c -fill $hlinescolor incr v } } #----------- # place the per cycle data on the lower level canvas. also draw the vertical grid lines on the # lower level canvas and generate the cycle number bar at the top of the lower level canvas. # set over1 [expr $cywidth*0.5] set over2 $cywidth set xtx $over1 set xln $over2 set xrc1 0 set xrc2 $over2 for {set i 0} {$i < $ncy} {incr i} { $f.c2 create rectangle ${xrc1}c 0 ${xrc2}c 0.5c -fill $cybarbg -outline $vlinescolor set cylabel "CY$i" $f.c2 create text ${xtx}c 0.25c -width ${cywidth}c -text $cylabel -fill $cybarfg -justify center -font $font2 $f.c2 create line ${xln}c 0 ${xln}c ${c1h}c -fill $vlinescolor set v 1 for {set k 0} {$k < $q} {incr k} { set sh [lindex $show $k] if {$sh == 1} { set item [lindex $cy($i) $k] $f.c2 create text ${xtx}c ${v}c -width ${cywidth}c -text $item -fill $fg2 -justify center -font $font2 incr v } } set xtx [expr $xtx+$cywidth] set xln [expr $xln+$cywidth] set xrc1 [expr $xrc1+$cywidth] set xrc2 [expr $xrc2+$cywidth] } unset show } ############################################# # main body of XsimView # #------- # parse the command line (either one argument -- sim output file name -- # or none. # global infile global t if {$argc == 0} { set infile "in.simv" puts "setting input file to in.simv\n" } elseif {$argc == 1} { set infile [lindex $argv 0] } else { puts "syntax is: xsimview \n" exit } ###################################################### ##### FILES NECESSARY FOR SAVING ###################################################### proc doSave {} { set file [fileSelect Write] catch {exec cp in.simv $file} } #################################################### ##### END FILES NECESSARY FOR SAVING #################################################### #------------------ # initialization sequence. # # parse the sim output file (only needs to be done once). # open the main window for XsimView # parse $infile wm title . "XsimView 0.1" button .go -text GO -command { strike findlongestvar readprefs drawcanvas } button .redraw -text ReDraw -command { destroy $t strike findlongestvar readprefs drawcanvas } button .save -text Save -command {doSave} button .quit -text Quit -command {destroy .} pack .go -side left -padx 2m -pady 2m pack .redraw -side left -padx 2m -pady 2m pack .save -side left -padx 2m -pady 2m pack .quit -side left -padx 2m -pady 2m