Search code examples
tcltext-processingvlsi

tcl text processing - rearrange values in rows and columns based on user defined value


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!!


Solution

  • 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.