I was investigating design for 100M/1G Ethernet MAC and found a good reference project from opencores.org, ethernet mac authored by Jon Gao. This blog is actually not about the Ethernet but about the Tcl GUI interface used in this project.
In my previous projects, I use Perl and Shell scripts quite a bit but not much for Tcl/Tk. If I need user to select an option, I will output all the options and prompt user to enter a digit to select which option to use. This is not too bad but a more user friendly way is to let user click on option directly. It becomes awkward when it comes to I give user option to modify many parameters since user has to go through prompts one by one.
When I studied Gao’s project, I realized he was using tcl/tk to implement a very convenient gui tool. How it is done is actually very straightforward and requires very minimum coding. You can easily follow this way to add gui to your project.
Project url at opencores is: http://opencores.org/project,ethernet_tri_mode
Once you download and unzip it, you can go to ethernet_tri_mode/sim/rtl_sim/ncsim_sim/script folder and run “wish run.tcl”. (Note here as many fpga designers I work on Windows PC and not Linux machine so I run “wish run.tcl” in a Cygwin terminal.) You can get below dialog box popped up:
run.tcl is as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
source user_lib.tcl source set_stimulus.tcl source set_reg_data.tcl source start_verify.tcl source batch_mode.tcl source filesel.tcl wm title . "main" frame .frame button .frame.b1 -width 20 -text "set_stimulus" button .frame.b2 -width 20 -text "set_cpu_data" button .frame.b3 -width 20 -text "start_verify" button .frame.b4 -width 20 -text "batch_mode" button .frame.b40 -width 20 -text "exit" bind .frame.b1 <Button-1> {set_stimulus} bind .frame.b2 <Button-1> {set_reg_data} bind .frame.b3 <Button-1> {start_verify 0 empty} bind .frame.b4 <Button-1> {batch_mode} bind .frame.b40 <Button-1> {exit} pack .frame .frame.b1 .frame.b2 .frame.b3 .frame.b4 .frame.b40 |
As seen above, the way to create this pop-up is very straightforward.
Another example is you can run “wish start.tcl” at ethernet_tri_mode/ and below window pops up:
click on “Next” and you get:
click on Verify and you get verification window again:
start.tcl is as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
variable MAC_SOURCE_REPLACE_EN variable MAC_TARGET_CHECK_EN variable MAC_BROADCAST_FILTER_EN variable MAC_TX_FF_DEPTH variable MAC_RX_FF_DEPTH variable header_data source sim/rtl_sim/ncsim_sim/script/run_proc.tcl frame .f1 frame .f2 text .f1.t1 -width 60 -height 20 button .f2.b1 -text "Quit" -width 5 -command {exit} button .f2.b2 -text "Next" -width 5 -command {module_conf} pack .f1 .f2 pack .f1.t1 pack .f2.b1 .f2.b2 -side left set strings \ {Hi guys, Thanks for your interest about this tri-speed ethernet MAC controller. Since this project was created at 25-Nov-2005,I put almost all my free time on this project.I am exhausted for this two month's extra work.If you think this project is useful,let me know that and i will feel much better. To increase the flexibility, some optional modules can be removed from the design to reduce area. Any problem or bug report please contact me by email: gaojon@yahoo.com jon 18-Jan-2006 } .f1.t1 insert end $strings proc module_conf {} { global MAC_SOURCE_REPLACE_EN global MAC_TARGET_CHECK_EN global MAC_BROADCAST_FILTER_EN global MAC_TX_FF_DEPTH global MAC_RX_FF_DEPTH global header_data if {[catch {open ./rtl/verilog/header.v r} fileid]} { puts "Failed open ./rtl/verilog/header.v file\n" } else { gets $fileid line if {[lindex $line 0]=="//"} { set line [lreplace $line 0 0] set MAC_SOURCE_REPLACE_EN 0 } else { set MAC_SOURCE_REPLACE_EN 1 } lappend header_data $line gets $fileid line if {[lindex $line 0]=="//"} { set line [lreplace $line 0 0] set MAC_TARGET_CHECK_EN 0 } else { set MAC_TARGET_CHECK_EN 1 } lappend header_data $line gets $fileid line if {[lindex $line 0]=="//"} { set line [lreplace $line 0 0] set MAC_BROADCAST_FILTER_EN 0 } else { set MAC_BROADCAST_FILTER_EN 1 } lappend header_data $line gets $fileid line set MAC_TX_FF_DEPTH [lindex $line 2] lappend header_data $line gets $fileid line set MAC_RX_FF_DEPTH [lindex $line 2] lappend header_data $line close $fileid } destroy .f1 .f2 frame .f1 frame .f2 frame .f1.f1 frame .f1.f2 frame .f1.f3 frame .f1.f4 frame .f1.f5 frame .f1.f6 pack .f1 .f2 pack .f1.f1 .f1.f2 .f1.f3 .f1.f4 .f1.f5 .f1.f6 label .f1.f1.lb -text "enable source MAC replace module" -width 30 checkbutton .f1.f1.cb -variable MAC_SOURCE_REPLACE_EN label .f1.f2.lb -text "enable target MAC check module " -width 30 checkbutton .f1.f2.cb -variable MAC_TARGET_CHECK_EN label .f1.f3.lb -text "enable broadcast packet filter module" -width 30 checkbutton .f1.f3.cb -variable MAC_BROADCAST_FILTER_EN label .f1.f4.lb -text "MAC_TX_FF_DEPTH" -width 30 entry .f1.f4.en -textvariable MAC_TX_FF_DEPTH -width 5 label .f1.f5.lb -text "MAC_RX_FF_DEPTH" -width 30 entry .f1.f5.en -textvariable MAC_RX_FF_DEPTH -width 5 button .f2.b1 -width 10 -text "Save" -command {save_header} button .f2.b2 -width 10 -text "Verify" -command {run_sim} button .f2.b4 -width 10 -text "Exit" -command {exit} pack .f1.f1.cb .f1.f1.lb -side right pack .f1.f2.cb .f1.f2.lb -side right pack .f1.f3.cb .f1.f3.lb -side right pack .f1.f4.en .f1.f4.lb -side right pack .f1.f5.en .f1.f5.lb -side right pack .f2.b1 .f2.b2 .f2.b4 -side left } proc save_header {} { global MAC_SOURCE_REPLACE_EN global MAC_TARGET_CHECK_EN global MAC_BROADCAST_FILTER_EN global MAC_TX_FF_DEPTH global MAC_RX_FF_DEPTH global header_data if {[catch {open ./rtl/verilog/header.v w} fileid]} { puts "Failed open ./rtl/verilog/header.v file\n" } else { set line [lindex $header_data 0] if {$MAC_SOURCE_REPLACE_EN==0} { set line [linsert $line 0 "//"] } puts $fileid $line set line [lindex $header_data 1] if {$MAC_TARGET_CHECK_EN==0} { set line [linsert $line 0 "//"] } puts $fileid $line set line [lindex $header_data 2] if {$MAC_BROADCAST_FILTER_EN==0} { set line [linsert $line 0 "//"] } puts $fileid $line set line [lindex $header_data 3] set line [lreplace $line 2 2 $MAC_TX_FF_DEPTH] puts $fileid $line set line [lindex $header_data 4] set line [lreplace $line 2 2 $MAC_RX_FF_DEPTH] puts $fileid $line close $fileid } } proc start_verify {} { cd sim/rtl_sim/ncsim_sim/script vish run.tcl } proc start_syn {} { cd syn synplify_pro syn.prj } proc run_sim {} { cd sim/rtl_sim/ncsim_sim/script/ run_proc } |
the code corresponding to invoking verify window is:
1 2 3 4 5 6 |
button .f2.b2 -width 10 -text "Verify" -command {run_sim} ... proc run_sim {} { cd sim/rtl_sim/ncsim_sim/script/ run_proc } |
run_proc.tcl in sim/rtl/ncsim_sim/script/ is as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
proc run_proc {} { source user_lib.tcl source set_stimulus.tcl source set_reg_data.tcl source start_verify.tcl source batch_mode.tcl source filesel.tcl toplevel .frame focus .frame wm title .frame "main" button .frame.b1 -width 20 -text "set_stimulus" button .frame.b2 -width 20 -text "set_cpu_data" button .frame.b3 -width 20 -text "start_verify" button .frame.b4 -width 20 -text "batch_mode" button .frame.b40 -width 20 -text "exit" -command {cd ../../../../ ;destroy .frame} bind .frame.b1 <Button-1> {set_stimulus} bind .frame.b2 <Button-1> {set_reg_data} bind .frame.b3 <Button-1> {start_verify 0 empty} bind .frame.b4 <Button-1> {batch_mode} pack .frame.b1 .frame.b2 .frame.b3 .frame.b4 .frame.b40 } |
As can be seen, it is very similar to run.tcl and the difference is due to run_proc.tcl is an procedure and called by others while run.tcl is a top and standalone module.
In summary, using tcl/tk to make an interactive gui tool is very convenient and can be very productive in asic/fpga design.