Where/how do the TCL variables saved? - tcl

I am looking at the TCL source code and trying to understand the mechanism how the TCL variables are managed internally. For example, given the following TCL script:
set a 1
set b 2
I looked at Tcl_SetObjCmd() function, it sets an object to interpreter and that is it. So when the first line runs, there is a Tcl_Obj is set to interpreter with value "1", but I do not find where this object is retrieved which leads to my ultimate goal, where does that object get stored?
Any pointer is greatly appreciated!

It's more complicated than it appears. The simple version is that the Tcl expression there would call Tcl_SetVar2Ex as something like Tcl_SetVar2Ex(interp, "a", NULL, Tcl_NewIntObj(1), 0) to setup a variable called 'a' in the interpreter with the value given. It will also assign this to to the interpreter's result using Tcl_SetObjResult.
However, modern Tcl does byte compilation and executes something else. We can examine this as shown below:
% tcl::unsupported::disassemble script {set a 1}
ByteCode 0x0x10e0110, refCt 1, epoch 3, interp 0x0xde9d00 (epoch 3)
Source "set a 1"
Cmds 1, src 7, inst 6, litObjs 2, aux 0, stkDepth 2, code/src 0.00
Commands 1:
1: pc 0-4, src 0-6
Command 1: "set a 1"
(0) push1 0 # "a"
(2) push1 1 # "1"
(4) storeScalarStk
(5) done
So the byte compiled version actually pushes the name and the value onto the stack then calls this storeScalarStk function. Some digging in the sources shows this gets executed in generic/tclExecute.c as INST_STORE_SCALAR_STK which basically just jumps to doCallPtrSetVar where it calls TclPtrSetVar which does a similar job to the Tcl_SetVar2Ex function from the public API. The main advantage of the byte compilation is on repeat runs where the syntactic parsing has already been handled so subsequent execution of a function is much faster than the first run.
Your basic question seems to be about how the value was returned to the interpreter. The interp structure has a result slot that is manipulated with Tcl_SetObjResult and Tcl_GetObjResult. Functions that want to return a result to script level assign a Tcl_Obj to the interp result.

Related

How to run repeatedly a proc in Tcl

I have written a proc in tcl which takes one argument (a positive integer) und displays a text. Lets call it permu (for permutation). I would like to execute this proc permanently, so
puts [permu 3]
and with the same argument (here 3), lets say every 2 or 3 seconds or so, without removing the previous outcome of the code. How can I do this?
The second question: Same question as above but I would like to clear the screen when the new outcome of permu is displayed.
Third question: In case that I decide to stop a running code (I work with Linux), for example the one above, how can I do this?
Thanks in advance!
Here's one way to do the repeated output:
proc repeat_permu {arg delay} {
puts [permu $arg]
after $delay [list repeat_permu $arg $delay]
}
# Note that the delay is in milliseconds
repeat_permu 3 3000
# Now start the event loop
vwait forever
To clear the screen you need to send the appropriate escape sequence before each new output. Most terminal emulators will accept the vt100 code, so you would do puts "\x1b[2J".
Normally you could just stop your program running by typing control-c, or do you want some means of doing this programmatically?
Update: A simpler way to do the repetition if you don't need to process any other events in parallel is just: while 1 {puts [permu 3]; after 3000}

Pass argument by reference TCL procedure

How to pass an argument by reference in the TCL procedure?
Look the following block of code:
proc test{&a} {
set a 7
}
set b 5
test b
puts $b
I waited that the output should be 7, but it was 5.
What is wrong with this code?
The trick is to use the upvar command to map the variable with that name in the caller's scope into the procedure's internal scope.
proc test {&a} {
# It's a good idea to use an explicit level of 1
upvar 1 ${&a} a
set a 7
}
I'd not normally name a variable &a, as they're annoying to use with $ syntax. I mostly prefer to use something like varName so that when someone reads the wrong-arguments error for test they get this readable version:
wrong # args: should be "test varName"

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.

TCL command wrapping

I am trying to write a TCL proc that will allow me to wrap statements and then 'execute' them internally.
For example, if I originally have :
set var $tmp
I want to have some procedure :
proc wrapper {command} {
puts "do something here"
puts $command #ie below : write "set var $tmp" to stdout
EXECUTE COMMAND HERE
puts "do something else here"
}
set void [wrapper {set var $tmp}]
My motivation is that I'm trying to write my own timing/profiler for individual statements.
Instead of "EXECUTE COMMAND", use
uplevel 1 $command
Note that if you attempt to time a script using a command like your wrapper, it won't be byte-compiled, which means that it won't perform the way it would inside a procedure.
Documentation: uplevel
You can give a try with time command which is already available with Tcl if your only intention is to just get the time taken by a set of code to execute.
time script ?count?
This command will call the Tcl interpreter count times to evaluate script (or once if count is not specified). It will then return a string of the form
503.2 microseconds per iteration
which indicates the average amount of time required per iteration, in microseconds. Time is measured in elapsed time, not CPU time.
Example :
set code_block {
foreach x "1 2 3" {
set y [expr {$x*20}];
}
}
puts [time $code_block 10]; # Executing the code for 10 times
which might produce random outputs such as
11.9 microseconds per iteration
Update 1 :
If you want to print the commands along with execution which is also possible. You can override the time command with rename command.
rename time _time; # Changing the 'time' to '_time'
# Defining your own 'time' command
proc time {command {count 1}} {
# Printing the commands here
puts $command
# Calling the actual '_time' command
# and returning that value from here
return [_time $command $count]
}
Place the above code at the top of your code and usage of time command after this will be using our customized procedure only.
They are passed as set of command which in turn, yes, a string. But, while the evaluation of the code, it will behave as if like how it will work in a tclsh.
As answered by Mr.Peter, if your code involves accessing the previous level of commands, variables, then you have to use uplevel or upvar based on your needs.
Reference : time, rename

Any difference of double quote a variable?

For the following code:
set str "a bb ccc"
if {[string first bb "$str"] >= 0} {
puts "yes"
}
My college said I should not double-quote $str because there is performance difference, something like TCL makes a new object internally using $str.
I cannot find a convincing document on this. Do you know if the claim is accurate?
Your colleague is actually wrong, as Tcl's parser is smart enough to know that "$str" is identical to $str. Let's look at the bytecode generated (this is with Tcl 8.6.0, but the part that we're going to look at in detail is actually the same in older versions all the way back to 8.0a1):
% tcl::unsupported::disassemble script {
set str "a bb ccc"
if {[string first bb "$str"] >= 0} {
puts "yes"
}
}
ByteCode 0x0x78710, refCt 1, epoch 15, interp 0x0x2dc10 (epoch 15)
Source "\nset str \"a bb ccc\"\nif {[string first bb \"$str\"] >= 0} "
Cmds 4, src 74, inst 37, litObjs 7, aux 0, stkDepth 2, code/src 0.00
Commands 4:
1: pc 0-5, src 1-18 2: pc 6-35, src 20-72
3: pc 15-20, src 25-46 4: pc 26-31, src 61-70
Command 1: "set str \"a bb ccc\""
(0) push1 0 # "str"
(2) push1 1 # "a bb ccc"
(4) storeScalarStk
(5) pop
Command 2: "if {[string first bb \"$str\"] >= 0} {\n puts \"yes\"\n}"
(6) startCommand +30 2 # next cmd at pc 36, 2 cmds start here
Command 3: "string first bb \"$str\""
(15) push1 2 # "bb"
(17) push1 0 # "str"
(19) loadScalarStk
(20) strfind
(21) push1 3 # "0"
(23) ge
(24) jumpFalse1 +10 # pc 34
Command 4: "puts \"yes\""
(26) push1 4 # "puts"
(28) push1 5 # "yes"
(30) invokeStk1 2
(32) jump1 +4 # pc 36
(34) push1 6 # ""
(36) done
As you can see (look at (17)–(19)), the "$str" is compiled to a push of the name of the variable and a dereference (loadScalarStk). That's the most optimal sequence given that there's no local variable table (i.e., we're not in a procedure). The compiler doesn't do non-local optimizations.
I think your colleague is correct: if Tcl sees plain $str where a word is expected, it parses out that "str" as the name of a variable, looks it up in the approptiate scope, then extracts an internal object representing its value from that variable and then asks that object to produce the string representation of that value. At this point that string representation will be either already available and cached (in the object) — and it will, in your case, — or it will be transparently generated by the object, and cached.
If you put dereferencing of a variable ($str) in a double quoted string, then Tcl goes like this: when it sees the first " in a place where a word is expected, it enters a mode where it would parse the following characters, performing variable- and command substitutions as it goes until it sees the next unescaped ", at which point the substituted text accumulated since the opening " is considered to be one word and it ends up being in a (newly created) internal object representing that word's value.
As you can see, in the second (your) case the original object holding the value of a variable named "str" will be asked for its value, and it then will be used to construct another value while in the first case the first value would be used right away.
Now there's a more subtle matter. For the scripts it evaluates, Tcl only guarantees that its interpreter obeys certain evaluation rules, and nothing more; everything else is implementation details. These details might change from version to version; for instance, in Tcl 8.6, the engine has been reimplemented using non-recursive evaluation (NRE), and while those were rather radical changes to the Tcl internals, your existing scripts did not notice.
What I'm leading you to, is that discussing of implicit performance "hacks" such as the one we're at now only have sense when applied to a particular version of the runtime. I very much doubt Tcl currently optimizes away "$str" to just re-use the object from $str but it could eventually start, in theory.
The real "problem" with your approach is not performance degradation but rather an apparent self-delusion you seem to apply to yourself which leads to Tcl code of dubious style. Let me explain. Contrary to "more conventional" languages (usually influenced by C and the like), Tcl does not have special syntax for strings. This is because it does not have string literals: every value starting its life in a script from a literal is initially a string. The actual type of any value is defined at runtime by commands operating on those values. To demonstrate, set x 10; incr x will put a string "10" to a variable named "x", and then the incr command will force the value in that variable "x" to convert the string "10" it holds to an integer (of value 10); then this integer will be incremented by 1 (producing 11) invalidating the string representation as a side effect. If you later will do puts $x, the string representation will be regenerated from the integer (producing "11"), cached in the value and then printed.
Hence the code style you adopted actually tries to make Tcl code look more like Python (or Perl or whatever was your previous language) for no real value, and also look alien to seasoned Tcl developers. Both double quotes and curly braces are used in Tcl for grouping, not for producing string values and code blocks, respectively — these are just particular use cases for different ways of grouping. Consider reading this thread for more background.
Update: various types of grouping are very well explained in the tutorial which is worth reading as a whole.