0

I am new to tcl and would like to use it in text processing of a simple case. The following format is in Liberty (.lib file) which is used in chip design. I would be truly indebted for any help on this.

Here is a snippet of my file (text processing to be done only on the "values")

timing () {  
    related_pin : "clk";  
    timing_type : setup_rising;  
    rise_constraint (constraint_template_5X5) {  
      index_1 ("0.01, 0.05, 0.12, 0.2, 0.4");  
      index_2 ("0.005, 0.025, 0.06, 0.1, 0.3");  
      index_3 ("0.084, 0.84, 3.36, 8.4, 13.44") ;  
      values ( \  
        "1.1, 1.2, 1.3, 1.4, 1.5", \        
        "2.1, 2.2, 2.3, 2.4, 2.5", \  
        "3.1, 3.2, 3.3, 3.4, 3.5", \  
        "4.1, 4.2, 4.3, 4.4, 4.5", \  
        "5.1, 5.2, 5.3, 5.4, 5.5", \  
        "6.1, 6.2, 6.3, 6.4, 6.5", \  
        "7.1 ,7.2, 7.3, 7.4, 7.5", \  
        "8.1, 8.2, 8.3, 8.4, 8.5", \  
        "9.1, 9.2, 9.3, 9.4, 9.5", \  
        "10.1,10.2,10.3,10.4,10.5", \  
        "11.1,11.2,11.3,11.4,11.5", \  
        "12.1,12.2,12.3,12.4,12.5", \  
        "13.1,13.2,13.3,13.4,13.5", \  
        "14.1,14.2,14.3,14.4,14.5", \  
        "15.1,15.2,15.3,15.4,15.5", \  
        "16.1,16.2,16.3,16.4,16.5", \  
        "17.1,17.2,17.3,17.4,17.5", \  
        "18.1,18.2,18.3,18.4,18.5", \  
        "19.1,19.2,19.3,19.4,19.5", \  
        "20.1,20.2,20.3,20.4,20.5", \  
        "21.1,21.2,21.3,21.4,21.5", \  
        "22.1,22.2,22.3,22.4,22.5", \  
        "23.1,23.2,23.3,23.4,23.5", \  
        "24.1,24.2,24.3,24.4,24.5", \  
        "25.1,25.2,25.3,25.4,25.5", \  
      );  
    } 

So all the "values" are in a 25 rows x 5 columns lookup table format which I wish to change to a 5 rows x 5 columns table format. To accomplish this, I would like to ask the user which of the 5 values in index_3 he/she wants before mapping is done as follows (while also removing the index_3 line):

C is the user defined column based on index_3: (column 1 for 0.084, column 2 for 0.84, column 3 for 3.36, column 4 for 8.4, column 5 for 13.44). *Only 1 value can be chosen by user

Mapping scheme:

   1,C -> row 1 column 1  
   2,C -> row 2 column 1  
   3,C -> row 3 column 1  
   4,C -> row 4 column 1  
   5,C -> row 5 column 1  
   6,C -> row 1 column 2  
   7,C -> row 2 column 2  
   8,C -> row 3 column 2

etc ..

For EXAMPLE, say a user chooses column 1 (value 0.084 from index_3) --> which 'chooses' entire 1st column of the data in 'values' to be text processed/arranged

Hence, resulting text processing done by tcl based on the mapping scheme should be:

index_1 ("0.01, 0.05, 0.12, 0.2, 0.4");  
index_2 ("0.005, 0.025, 0.06, 0.1, 0.3");

 values ( \  
        "1.1, 6.1, 11.1, 16.1, 21.1", \  
        "2.1, 7.1, 12.1, 17.1, 22.1", \  
        "3.1, 8.1, 13.1, 18.1, 23.1", \  
        "4.1, 9.1, 14.1, 19.1, 24.1", \  
        "5.1, 10.1,15.1, 20.1, 25.1", \  
  ); 

My strategy is this:

  1. search for "rise_constraint" to zero in on which values to text process in the entire file

  2. comment out line "index_3 (...)"; add /* and */ at beginning and end of line in reprinted processed file (optional)

  3. convert 'values' from 25 rows x 5 columns table to 5 rows x 5 columns table BASED on Index_3 value chosen ("user defined column choice")

  4. re-print other lines as is (including the text processed 'values')

I tried my very best to explain my coding request. Can any of you meastros please help me think of a proper way in tcl I can do such text processing? Many thanks!!

JigarGandhi
  • 243
  • 6
  • 18
edaloke
  • 31
  • 6

3 Answers3

1

The first step is to know how to represent the data in tcl in its final form. This is just one possible solution.

dict set risedata constraints { 
  constraint {
    0.084 { 0 { 0 1.1 1  6.1 2 11.1 3 16.1 4 21.1 }
            1 { 0 2.1 1  7.1 2 12.1 3 17.1 4 22.1 }
            2 { 0 3.1 1  8.1 2 13.1 3 18.1 4 23.1 }
            3 { 0 4.1 1  9.1 2 14.1 3 19.1 4 24.1 }
            4 { 0 5.1 1 10.1 2 15.1 3 20.1 4 25.1 }
    }
  }
  indexes { 1 { 0.01 0.05 0.12 0.2 0.4 }
            2 { 0.005 0.025 0.06 0.1 0.3 }
            3 { 0.084 0.84 3.36 8.4 13.44 } }
}

set c 0.084
puts "$c 2 3: [dict get $risedata constraints constraint $c 2 3]"
puts "idx1 3: [lindex [dict get $risedata constraints indexes 1] 3]"
puts "idx2 3: [lindex [dict get $risedata constraints indexes 2] 3]"

Then knowing where you need to be, loading the .lib is just a straightforward parsing problem:

set fh [open z.lib r]
set inval false
while { [gets $fh line] >= 0 } {
  if { [regexp {\);} $line] } {
    set inval false
  }
  if { [regexp {index_(\d+)} $line all idx] } {
    regsub {^[^"]*"} $line {} d
    regsub {".*} $d {} d
    regsub -all {,} $d {} d
    dict set risedata constraints indexes $idx $d
  }
  if { $inval } {
    regsub {^[^"]*"} $line {} d
    regsub {".*} $d {} d
    regsub -all {[ ,]+} $d { } d
    set row [expr {$rcount % 5}]
    set column [expr {$rcount / 5}]
    set i 0
    foreach {v} [split $d { }] {
      set c [lindex [dict get $risedata constraints indexes 3] $i]
      dict set risedata constraints constraint $c $row $column $v
      incr i
    }
    incr rcount
  }
  if { [regexp {values} $line] } {
    set inval true
    set row 0
    set rcount 0
  }
}
close $fh

puts $risedata
set c 0.084
puts "$c 2 3: [dict get $risedata constraints constraint $c 2 3]"
puts "idx1 3: [lindex [dict get $risedata constraints indexes 1] 3]"
puts "idx2 3: [lindex [dict get $risedata constraints indexes 2] 3]"
Brad Lanam
  • 5,192
  • 2
  • 19
  • 29
  • That was phenomenal!! Clear and concise. Thank you so very much. I ran the script for now and it returns what I exactly asked for! You are a LIFESAVER! I will have to definitely take some time to understand what you rustled up - the 'dict' and 'regexp' commands are new to me. Seems like dict is used in the way you would handle arrays. One tiny question, how do you propose I can overwrite my original.lib file 'values' with the updated 'values' returned by your variable 'constraints' (after the user inputs his index_3 value)? Also, do let me know how I can ref you for my project. Thx – edaloke Apr 15 '14 at 04:33
  • If you need to create a new .lib file, you will need to upgrade the parser above to handle the rest of the fields. Once you have all the needed values in the dictionary, writing out a new .lib file is pretty easy. References: [dict man page](http://www.tcl.tk/man/tcl8.5/TclCmd/dict.htm) [regexp man page](http://www.tcl.tk/man/tcl8.5/TclCmd/regexp.htm) [regsub man page](http://www.tcl.tk/man/tcl8.5/TclCmd/regsub.htm). For writing out a file, you would use open, close, dict, foreach, puts. – Brad Lanam Apr 15 '14 at 06:41
  • Thanks a lot for the references and strategy! I've tried to follow your advice but have not a find a way to replace the values with the new values including the ',' and '\' characters and arranging the values in a 5x5 format (as shown in my question). This is only last piece of help I earnestly ask of you. I will reference your name on my project. This has been of immense help! – edaloke Apr 15 '14 at 17:23
  • I am struggling to remove the indexes between the values in say {0 3.1 1 8.1 2 13.1 3 18.1 4 23.1} and change it to {3.1 8.1 13.1 18.1 23.1} before overwriting this in .lib file. After removing the indexes, I will have to insert the ',' and '\' at the end for each line (iterated through a for loop). You inputs will be extremely appreciated. Sorry to bother you so much but I don't think I've ever come across a tcl maestro like you so far and hence I'll get to learn a lot from your inputs. Thanks! – edaloke Apr 15 '14 at 19:53
  • @edaloke if the answer solves your problem, you can accept it as the correct answer, at least as a courtesy to the answerer if nothing else... ;) – msb Apr 15 '14 at 23:21
  • @msb The answer is definitely correct and I have courteously acknowledged it (check comments please). I am following up on a continuation of the problem. I'm just requesting for further inputs from Brad if he has the time. Behooves me to learn from the best. – edaloke Apr 15 '14 at 23:43
  • @edaloke I'm talking about marking it as correct, it's the green checkmark below the votecount. :) Don't worry, you can keep discussing after you accept it, or you can create another question and direct him there, if it's reasonably different from your original question. I'm not saying you're doing it wrong, just explaining the common use of stackoverflow from what I know. (Y) – msb Apr 15 '14 at 23:47
  • 1
    @msb Thanks for the clarification :) I just joined stackoverflow yesterday so I did not know about the green checkmark option (until now that is). I have marked it as correct. My apologies if I misunderstood you. I shall redirect Brad to my follow up link. Good day! – edaloke Apr 15 '14 at 23:51
  • @edaloke welcome to SO. :) I saw you were new, that's why I decided to clarify. Not everyone is on this site for the points, but marking as correct is usually seen as a combination of showing gratitude and awarding points all at once. ;) Good day! – msb Apr 16 '14 at 00:00
  • You can put multiple variable names in a foreach loop: `foreach {k v} [dict get $risedata constraints constraint 0.084 0] { puts -nonewline "$v, " } puts " \\"` Just wrap this in another loop to increment the main index from 0 to 5. – Brad Lanam Apr 16 '14 at 15:53
1

I finally got really interested in this, and against my better judgement coded up a complete answer. I'm not going to document it in any way. Read it, read the docs for commands you don't understand, then come and ask questions.

Looking at your liberty file, I see it's very close to native Tcl syntax. So you can create a few procedures named "timing", "rise_constraint", etc, and you can basically run it as a script.

package require struct::list

######################################################################
proc main {libfile} {
    global lines idx3 vals
    set lines [list]
    set idx3 [list]
    set vals [list]

    evaluate_liberty $libfile

    set idx [get_choice "select an index_3 value: " $idx3]
    set column [struct::list mapfor elem $vals {lindex $elem $idx}]

    set newvalues [list]
    for {set i 0} {$i < 5} {incr i} {
        lappend newvalues [lrange $column [expr {5*$i}] [expr {5*($i+1)-1}]]
    }

    print_liberty $newvalues
}

######################################################################
proc evaluate_liberty {libfile} {
    set fh [open $libfile r]
    # handle known syntax error in liberty file
    set contents [string map {\", \"} [read -nonewline $fh]]
    regsub -all -line {\s+$} $contents {} contents
    close $fh

    uplevel #0 $contents
}

proc get_choice {prompt values} {
    while {1} {
        for {set i 0} {$i < [llength $values]} {incr i} {
            puts stderr [format "%2d. %s" $i [lindex $values $i]]
        }
        puts -nonewline stderr $prompt
        gets stdin answer
        if {[string is integer -strict $answer]} {
            if {0 <= $answer && $answer < [llength $values]} {
                return $answer
            }
        }
    }
}

proc print_liberty {newvalues} {
    global lines close_braces
    puts [join $lines \n]

    puts "values ( \\"
    foreach elem $newvalues {
        puts [format "\"%s\", \\" [join $elem {, }]]
    }
    puts ");"

    for {set i 1} {$i <= $close_braces} {incr i} {
        puts [format %c 125]
    }
}

######################################################################
# define DSL
proc timing {label script} {
    lappend ::lines [format "timing %s %c" $label 123]
    incr ::close_braces
    uplevel 1 $script
}

proc rise_constraint {label script} {
    lappend ::lines [format "rise_constraint %s %c" $label 123]
    incr ::close_braces
    uplevel 1 $script
}

proc index_3 {args} {
    global idx3
    foreach item $args {
        lappend idx3 [string map {( "" \" "" , "" ) ""} $item]
    }
}

proc values {args} {
    global vals
    foreach set [lrange $args 1 end-1] {
        lappend vals [split [regsub -all {\s*,\s*} $set { }]]
    }
}

rename unknown system_unknown

proc unknown args {
    lappend ::lines "[join $args];"
}

######################################################################
if {$argc == 0} {
    puts "usage: $argv0 filename.lib"
    exit
}

set libfile [lindex $argv 0]
if {![file exists $libfile]} {
    error "no such file: $libfile"
}

main $libfile

Running it:

$ tclsh liberty.tcl test.lib  > newlib.lib
 0. 0.084
 1. 0.84
 2. 3.36
 3. 8.4
 4. 13.44
select an index_3 value: 0

$ cat newlib.lib 
timing () {
related_pin : clk;
timing_type : setup_rising;
rise_constraint (constraint_template_5X5) {
index_1 ("0.01, 0.05, 0.12, 0.2, 0.4");
index_2 ("0.005, 0.025, 0.06, 0.1, 0.3");
values ( \
"1.1, 2.1, 3.1, 4.1, 5.1", \
"6.1, 7.1, 8.1, 9.1, 10.1", \
"11.1, 12.1, 13.1, 14.1, 15.1", \
"16.1, 17.1, 18.1, 19.1, 20.1", \
"21.1, 22.1, 23.1, 24.1, 25.1", \
);
}
}

You do lose all your nice indentation, but you get the values you want.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • For your reading, Tcl syntax can be completely described on one page: http://tcl.tk/man/tcl8.5/TclCmd/Tcl.htm -- read that a few times to help you understand my code. – glenn jackman Apr 17 '14 at 00:42
  • WOW!!! This is absolutely astounding stuff. You're a magician! I think it would have taken me a few eons to code this. I'm more of a hardware description coder (Verilog etc). Anyway, I will have to read up on the commands - some it them are very alien to me. Thanks so much your time and effort, I'm very appreciative. I will follow up with queries.. I tried running it in activetcl, somehow it crashes...hmm. – edaloke Apr 17 '14 at 06:15
0

This is STF (Synopsys Technology File) or simple known as [dot]lib liberty

I did work on these files on TCL platform but by use of some Parsers.

although many parser are available here is one for you.

I suggest to use parsers for handling [dot]lib as many times the file formate gets corrupt and is unable to read by primetime or other lib tool.

liberty_parser

there is another one available in C language too .. google vlsicad ucsd edu + liberty parser

JigarGandhi
  • 243
  • 6
  • 18