Does TCL have macro like LISP - tcl

In LISP, one can define a macro, which expands at the same stack level, and not add to the stack. In TCL, one can call a procedure, and then use uplevel 1. I have seen it to cause in some instances a significant slow-down. What I would like is a macro-like expansion. Is that available in TCL? I could not find anything to suggest that it does.

Macros aren't used that much, since uplevel, upvar and tailcall give a suite of capabilities that do fairly well. In particular, using uplevel doesn't prevent bytecode compilation (subject to a few technical restrictions, and providing you're using 8.6 or later) so you can go ahead and use that without worrying too much. Which isn't to say that macros can't be done, but they're done by introducing on the outside a command that creates commands (probably procedures) that have had macro expansion performed on them during their definition. Once you work that way, you can do quite radical rearrangements of the code; the input text does not need to look like Tcl at all.
There are a few pages on the Tcler's Wiki on the topic; http://wiki.tcl.tk/3888, http://wiki.tcl.tk/11156, etc. A sort-of macro system I've written and that I'm using live is embedded within tclquadcode. With that, I'm able to use pseudo-assembly basic block labels as macro-like things: that involves not just a simple replacement, but rather relocating the code concerned to the beginning of the script body so that I don't need to manually declare the variables concerned up front (which I'd previously found to be horribly error-prone and hard to read); it's internally using lambda terms rather than procedures for scoping control, but the difference isn't too important here given that label is the macro, not build. Here's an example of use of that code. The relevant bit of that code is (if I do typical replacements for readability):
build {
my condBr [my and [my isNumericInt $x] [my isNumericInt $y]] \
$ints $doubles
label ints:
my ret [my add(INT,INT) [my numeric.int $x] [my numeric.int $y]]
label doubles:
set left [my cast(DOUBLE) $x "left"]
set right [my cast(DOUBLE) $y "right"]
my ret [my add(DOUBLE,DOUBLE) $left $right]
}
That's internally rewritten quite a bit, to approximately this:
apply {{func x y} {
set ints [$func block "ints"]
set doubles [$func block "doubles"]
my SetCurrentBasicBlock [$func getEntryBlock]
my condBr [my and [my isNumericInt $x] [my isNumericInt $y]] \
$ints $doubles
my SetCurrentBasicBlock $ints
my ret [my add(INT,INT) [my numeric.int $x] [my numeric.int $y]]
my SetCurrentBasicBlock $doubles
set left [my cast(DOUBLE) $x "left"]
set right [my cast(DOUBLE) $y "right"]
my ret [my add(DOUBLE,DOUBLE) $left $right]
}} $func $x $y
Very approximately. The real version of the generated code is a lot more complex because of debugging metadata tracking; this is pretty hefty code generation under the hood.

Related

How to access VHDL signal attributes in ModelSim via TCL?

I am developing a CPU in VHDL. I am using ModelSim for simulation and testing. In the simulation script I load a program from a binary file to the instruction memory. Now I want to automatically check if the program fits into memory and abort simulation if it doesn't. Since the memory is basically an array of std_logic_vectors, all I would have to do is read the corresponding signal attribute for use in a comparison. My problem is: How do I access a VHDL signal attribute in TCL inside ModelSim?
The closest I have gotten so far is to use the describe command:
describe sim/:tb:uut:imem:mem_array
which prints something like
# Array(0 to 255) [length 256] of
# Array(31 downto 0) [length 32] of
# VHDL standard subtype STD_LOGIC
Now, of course I could parse the length out of there via string operations. But that would not be a very generic solution. Ideally I would like to have something like this:
set mem_size [get_attribute sim/:tb:uut:imem:mem_array'length]
I have searched stackoverflow, googled up and down and searched through the commands in the command reference manual, but I could not find a solution. I am confident there must be a rather easy solution and I just lack the proper wording to successfully search for it. To me, this doesn't look overly specific and I am sure this could come in hand on many occasions when automating design testing. I am using version 10.6.
I would be very grateful if an experienced ModelSim user could help me out.
Disclaimer: I'm not a Tcl expert, so there's probably a more optimized solution out there.
There's a command called examine that you can use to get the value of obejcts.
I created a similar testbench here with a 256 x 32 array, the results were
VSIM> examine -radix hex sim/:tb:uut:imem:mem_array
# {32'hXXXXXXXX} {32'hXXXXXXXX} {32'hXXXXXXXX} {32'hXXXXXXXX} {32'hXXXXXXXX} ...
This is the value of sim/:tb:uut:imem:mem_array at the last simulation step (i.e.,
now).
The command return a list of values for each match (you can use wildcards), so
in our case, it's a list with a single item. You can get the depth by counting
the number of elements it returns:
VSIM> llength [lindex [examine sim/:tb:uut:imem:mem_array] 0]
# 256
You can get the bit width of the first element by using examine -showbase -radix hex,
which will return 32'hFFFFFFFF, where 32'h is the part you want to parse. Wrapping
that into a function would look like
proc get_bit_width { signal } {
set first_element [lindex [lindex [examine -radix hex -showbase $signal] 0] 0]
# Replace everything after 'h, including 'h itself to return only the base
return [regsub "'h.*" $first_element ""]
}
Hope this gives some pointers!
So, I actually found an easy solution. While further studying of the command reference manual brought to light that it is only possible to access a few special signal attributes and length is not one of them, I noticed that ModelSim automatically adds a size object to its object database for the memory array. So I can easily use
set ms [examine sim/:tb:uut:imem:mem_array_size]
to obtain the size and then check if the program fits.
This is just perfect for me, elegant and easy.

Variable substitution in TCL inside curly brace

I am trying to perform a variable substitution within curly braces which I understand is not feasible directly in TCL.
The code I am trying:
interface code -clip {$x1 $y1 $x2 $y2}
The interface code is a third party TCL function that has an option called clip which reads x1,y1,x2,y2 coordinates within curly braces.
I need to feed in the values of x1,y2 within the curly braces.
In Tcl, the {…} syntax technically means “do no substitutions at all on this”. The command that the value gets passed to might do its own thing with those characters, and those things might look a lot like substitution (e.g., if it is evaluating the word as a script, which is what if and for and while and … do), but Tcl's core syntax just leaves them alone and passes them through.
To get what you want, there's a few alternatives. You can do the substitutions before passing the value in — the command literally can't see that you've done this if you choose to do it — or you can get the command to do the substitutions (not a useful suggestion if it's third-party code, of course).
To do the substitutions yourself, you might do one of these:
interface code -clip [list $x1 $y1 $x2 $y2]
interface code -clip "$x1 $y1 $x2 $y2"
interface code -clip [subst {$x1 $y1 $x2 $y2}]
The first option is good if the command takes a list (which your case looks like; it appears to be a coordinate list). The second option is good if the command takes a string. The third option is good when things are getting complicated! It's usually a good idea to try to avoid very complicated stuff; programs are difficult enough to write and read without deliberately making them even harder to understand.

invalid command name "2001:172:16:21::36"

I need to assign my final variable with the following string UDP6:[2001:172:16:21::36]
set ipAddr1 "UDP6,2001:172:16:21::36"
set ipAddrArr [split $ipAddr1 ","]
set ipAddrArr11 [lindex $ipAddrArr 0]
set ipAddrArr12 [lindex $ipAddrArr 1]
set tmp ":\["
set ipAddr1Part [join "$ipAddrArr11 $ipAddrArr12" $tmp]
set tmp1 "]"
set ipAddrFinal [join "$ipAddr1Part$tmp1"]
When I run the tcl script, it gives invalid command name as 2001:172:16:21::36.
I have printed ipAddrFinal value , it gives the expected one UDP6:[2001:172:16:21::36]
pls help me out? what am I missing
The script as you have written it works fine; it assigns the string UDP6:[2001:172:16:21::36] to the variable ipAddrFinal. However, since it contains characters that are Tcl metacharacters in some contexts, I suspect that you are then using the string in an unsafe way, most likely with eval or possibly with subst or uplevel. If you look at the stack trace of the error (in the errorInfo global variable by default) you should be told pretty exactly where the offending code is; it might give a few places you need to look, but it usually isn't too hard to hunt down where the problem originates from.
If your problem comes from uplevel, you are probably going to need to use list to construct a command; 99.99% of all problems with uplevel are handled that way. If your problems come from eval, the chance's good that you need to switch to using expansion syntax. If your problems come from subst or are otherwise still deeply confusing, check back with us (with your stack trace if you are not sure where the problem is coming from).
Example of a fix for eval:
Change:
set action "puts \"IP\\ address\\ is\\ $ipAddrFinal\""
eval $action
to:
set action [list puts "IP address is $ipAddrFinal"]
{*}$action
NB: The error from doing the eval is a reasonable example too:
invalid command name "2001:172:16:21::36"
while executing
"2001:172:16:21::36"
("eval" body line 1)
invoked from within
"eval $action"
Note that it says that it's in an eval, and that points squarely to unsafe script construction. The list command does safe script construction as one of its bonus superpowers.

tcl scripts, struggling with [...] and [expr ...]

I can't understand how assignments and use of variables work in Tcl.
Namely:
If I do something like
set a 5
set b 10
and I do
set c [$a + $b]
Following what internet says:
You obtain the results of a command by placing the command in square
brackets ([]). This is the functional equivalent of the back single
quote (`) in sh programming, or using the return value of a function
in C.
So my statement should set c to 15, right?
If yes, what's the difference with
set c [expr $a + $b]
?
If no, what does that statement do?
Tcl's a really strict language at its core; it always follows the rules. For your case, we can therefore analyse it like this:
set c [$a + $b]
That's three words, set (i.e., the standard “write to a variable” command), c, and what we get from evaluating the contents of the brackets in [$a + $b]. That in turn is a script formed by a single command invocation with another three words, the contents of the a variable (5), +, and the contents of the b variable (10). That the values look like numbers is irrelevant: the rules are the same in all cases.
Since you probably haven't got a command called 5, that will give you an error. On the other hand, if you did this beforehand:
proc 5 {x y} {
return "flarblegarble fleek"
}
then your script would “work”, writing some (clearly defined) utter nonsense words into the c variable. If you want to evaluate a somewhat mathematical expression, you use the expr command; that's it's one job in life, to concatenate all its arguments (with a space between them) and evaluate the result as an expression using the documented little expression language that it understands.
You virtually always want to put braces around the expression, FWIW.
There are other ways to make what you wrote do what you expect, but don't do them. They're slow. OTOH, if you are willing to put the + first, you can make stuff go fast with minimum interference:
# Get extra commands available for Lisp-like math...
namespace path ::tcl::mathop
set c [+ $a $b]
If you're not a fan of Lisp-style prefix math, use expr. It's what most Tcl programmers do, after all.
set c [$a + $b]
Running the above command, you will get invalid command name "5" error message.
For mathematical operations, we should rely on expr only as Tcl treats everything as string.
set c [expr $a + $b]
In this case, the value of a and b is passed and addition is performed.
Here, it is always safe and recommended to brace the expressions as,
set c [expr {$a+$b}]
To avoid any possible surprises in the evaluation.
Update 1 :
In Tcl, everything is based on commands. It can a user-defined proc or existing built-in commands such as lindex. Using a bare-word of string will trigger a command call. Similarly, usage of [ and ] will also trigger the same.
In your case, $a replaced with the value of the variable a and since they are enclosed within square brackets, it triggers command call and since there is no command with the name 5, you are getting the error.

Button with multiple command line arguments

Can a button in tcl could be linked to multiple command line arguments ?
I have a code which runs when a button is clicked, a progressbar code with time in seconds is also linked with it , and should start at same time when this button is pressed.
I put both procs as a command in button command argument using {} but it fails with Error.
Code Snippet
button .b -image $p -command {progressbar 300 run_structural_comparision}
proc progressbar {seconds} {
ttk::progressbar .pg -orient horizontal -mode determinate -maximum $seconds
pack .pg -side left
update idletasks
# Do some real work here for $seconds seconds
for {set i 0} {$i < $seconds} {incr i} {
after 1000; # Just waiting in this example, might as well do something useful here
.pg step; # After some part of the work, advance the progressbar
update idletasks; # Needed to update the progressbar
}
# Done, clean up the dialog and progressbar
}
proc run_structural_comparision {} {
type_run
global ENTRYfilename ENTRYfilename2 curDIR curDIR2 typep reflib compLib rundir hvt_verilog logfile
set path [concat $reflib $compLib]
## set path [concat $ENTRYfilename $ENTRYfilename2]
puts $path
set str "compare_structure -overlap_when -type {timing constraint} -report compare_structure_"
set trt ".txt"
set structure [concat [string trim $str][string trim $typep][string trim $trt] $path]
puts $structure
puts $rundir
cd $rundir
set filename [concat "compare_structure_" $typep ".tcl"]
if {[ file exists $rundir/$filename] == 1 } {
exec rm -rf $rundir/compare_structure_$typep.tcl
}
A button's -command callback is a Tcl script. It will be evaluated at the global level of the stack. If you want to run two commands, you can just put a script in there to run the two commands:
button .b -command { command_1; command_2 }
This will run them sequentially. Tcl is naturally single-threaded as that is by far the easiest programming model for people to work with. It's possible to make code that works by doing callbacks to appear to be doing multiple things at once (that's how Tk works, just like virtually all other GUIs on the planet) but you're really only doing one thing at a time.
But your real question…
The core of what you need is a way to run the program that takes a long time in the background so that you can monitor it and continue to update the GUI. This is not a trivial topic, unfortunately, and the right answer will depend on exactly what is going on.
One of the simplest techniques is where the CPU-bound processing is done in a subprocess. In that case, you can run the subprocess via a pipeline and set fileevent to give you a notification callback when output is produced or the program terminates. Tcl is very good at this sort of thing; things that many languages have as very advanced techniques just feel natural when done with Tcl, as a great deal of thought has been put into how to make I/O callbacks work nicely.
If it's in-process and long-running without the opportunity for callbacks, things get more complex as you have to have the processing and the GUI updates in different threads. Which isn't too hard if you've got everything set up right, but which might require substantial re-architecting of your program (as it is usual for threads in Tcl to be extremely strongly partitioned from each other).
The simplest thing to do is to create a procedure that calls the two functions. If you wantie:
proc on_button_press {seconds} {
after idle [list progressbar $seconds]
after idle [list run_structural_comparision]
}
You can put multiple calls in the immediate button handler command string but it quickly gets complicated. But in short, use a semicolon to separate the two commands.
Your use if update idletasks should be considered a "code smell". ie: avoid it. In this case, in the progressbar function, setup the bar then just have everything else called by after calls to update the state of the progress.
I suspect your rm -rf may not do what you want. It it likely to lockup the interface as you get nothing back until the command has completed. Better is to write a function to walk the directory tree and delete the files with file delete and you can then raise progress events as you go and keep the UI alive by breaking up the processing into chunks using after again.