1 /** @page primertcl OpenOCD TCL Primer
5 ****************************************
6 ****************************************
8 This is a short introduction to 'un-scare' you about the language
9 known as TCL. It is structured as a guided tour through the files
10 written by me [Duane Ellis] - in early July 2008 for OpenOCD.
12 Which uses the "JIM" embedded Tcl clone-ish language.
14 Thing described here are *totally* TCL generic... not Jim specific.
16 The goal of this document is to encourage you to add your own set of
17 chips to the TCL package - and most importantly you should know where
18 you should put them - so they end up in an organized way.
23 ****************************************
24 ****************************************
26 Adding "chip" support - Duane Ellis July 5 - 2008.
29 In your "openocd.cfg" file add something like this:
31 source [find tcl/chip/VENDOR/FAMILY/NAME.tcl]
34 source [find tcl/chip/atmel/at91/at91sam7x256.tcl]
36 You'll notice that it makes use of:
38 tcl/cpu/arm/<NAME>.tcl.
40 Yes, that is where you should put "core" specific things.
41 Be careful and learn the difference:
43 THE "CORE" - is not the entire chip!
46 That "file" listed above is called a "CHIP FILE".
48 It may be standalone, or may need to "source" other "helper" files.
50 The reference [7/5/2008] is the at91sam7x256.tcl file.
52 ****************************************
53 ****************************************
55 Open: at91sam7x256.tcl
58 A walk through --- For those who are new to TCL.
60 Examine the file: at91sam7x256.tcl
63 source [find path/filename.tcl]
65 In TCL - this is very important.
67 Rule #1 Everything is a string.
68 Rule #2 If you think other wise See #1.
70 Rule #1: The wife is correct.
71 Rule #2: If you think otherwise, See #1
73 Any text contained inside of [square-brackets]
74 is just like `back-ticks` in BASH.
76 Hence, the [find FILENAME] executes the command find with a single
77 parameter the filename.
79 ========================================
81 Next you see a series of:
85 It is mostly "obvious" what is going on.
87 Exception: The arrays.
89 You would *THINK* Tcl supports arrays.
90 In fact, multi-dim arrays. That is false.
92 For the index for"FLASH(0,CHIPSELECT)" is actually the string
93 "0,CHIPSELECT". This is problematic. In the normal world, you think
94 of array indexes as integers.
96 For example these are different:
101 Why? Because 0x0c {lowercase} is a string.
102 Don't forget UPPER CASE.
104 You must be careful - always... always... use simple decimal
105 numbers. When in doubt use 'expr' the evaluator. These are all the
109 set foo([expr $x]) "twelve"
112 set foo([expr $x]) "twelve"
115 set foo([expr $x]) "twelve"
117 **************************************************
118 ***************************************************
120 Open the file: "bitsbytes.tcl"
122 There is some tricky things going on.
125 First, there is a "for" loop - at level 0
126 {level 0 means: out side of a proc/function}
128 This means it is evaluated when the file is parsed.
130 == SIDEBAR: About The FOR command ==
131 In TCL, "FOR" is a funny thing, it is not what you think it is.
133 Syntactically - FOR is a just a command, it is not language
134 construct like for(;;) in C...
136 The "for" command takes 4 parameters.
137 (1) The "initial command" to execute.
138 (2) the test "expression"
139 (3) the "next command"
140 (4) the "body command" of the FOR loop.
142 Notice I used the words "command" and "expression" above.
145 1) executes the "initial command"
146 2) evaluates the expression if 0 it stops.
147 3) executes the "body command"
148 4) executes the "next command"
151 As show, each of these items are in {curly-braces}. This means they
152 are passed as they are - KEY-POINT: un evaluated to the FOR
153 command. Think of it like escaping the backticks in Bash so that the
154 "under-lying" command can evaluate the contents. In this case, the FOR
157 == END: SIDEBAR: About The FOR command ==
159 You'll see two lines:
162 set vn [format "BIT%d" $x]
164 Format is like "sprintf". Because of the [brackets], it becomes what
165 you think. But here's how:
167 First - the line is parsed - for {braces}. In this case, there are
168 none. The, the parser looks for [brackets] and finds them. The,
169 parser then evaluates the contents of the [brackets], and replaces
170 them. It is alot this bash statement.
175 set $vn [expr (1024 * $x)]
178 In line 1, we dynamically created a variable name. Here, we are
179 assigning it a value. Lastly Line 3 we force the variable to be
180 global, not "local" the the "for command body"
185 proc create_mask { MSB LSB } {
189 Like "for" - PROC is really just a command that takes 3 parameters.
190 The (1) NAME of the function, a (2) LIST of parameters, and a (3) BODY
192 Again, this is at "level 0" so it is a global function. (Yes, TCL
193 supports local functions, you put them inside of a function}
195 You'll see in some cases, I nest [brackets] alot and in others I'm
196 lazy or wanted it to be more clear... it is a matter of choice.
200 **************************************************
201 ***************************************************
203 Open the file: "memory.tcl"
206 Here is where I setup some 'memory definitions' that various targets can use.
208 For example - there is an "unknown" memory region.
210 All memory regions must have 2 things:
214 And the array must have some specific names:
216 Where: THING is one of:
222 RWX - the access ability.
223 WIDTH - the accessible width.
225 ie: Some regions of memory are not 'word'
228 The function "address_info" - given an address should
229 tell you about the address.
231 [as of this writing: 7/5/2008 I have done
232 only a little bit with this -Duane]
238 proc memread32 { ADDR }
239 proc memread16 { ADDR }
240 proc memread8 { ADDR }
242 All read memory - and return the contents.
244 [ FIXME: 7/5/2008 - I need to create "memwrite" functions]
246 **************************************************
247 ***************************************************
249 Open the file: "mmr_helpers.tcl"
252 This file is used to display and work with "memory mapped registers"
254 For example - 'show_mmr32_reg' is given the NAME of the register to
255 display. The assumption is - the NAME is a global variable holding the
258 The code does some tricks. The [set [set NAME]] is the TCL way
259 of doing double variable interpolation - like makefiles...
261 In a makefile or shell script you may have seen this:
263 FOO_linux = "Penguins rule"
264 FOO_winXP = "Broken Glass"
265 FOO_mac = "I like cat names"
271 FOO = ${FOO_${BUILD}}
273 The "double [set] square bracket" thing is the TCL way, nothing more.
277 The IF statement - and "CATCH" .
279 Notice this IF COMMAND - (not statement) is like this:
280 [7/5/2008 it is this way]
282 if ![catch { command } msg ] {
285 error [format string...]
288 The "IF" command expects either 2 params, or 4 params.
290 === Sidebar: About "commands" ===
292 Take a look at the internals of "jim.c"
293 Look for the function: Jim_IfCoreCommand()
294 And all those other "CoreCommands"
296 You'll notice - they all have "argc" and "argv"
298 Yea, the entire thing is done that way.
300 IF is a command. SO is "FOR" and "WHILE" and "DO" and the
301 others. That is why I keep using the phase it is a "command"
303 === END: Sidebar: About "commands" ===
305 Parameter 1 to the IF command is expected to be an expression.
307 As such, I do not need to wrap it in {braces}.
309 In this case, the "expression" is the result of the "CATCH" command.
311 CATCH - is an error catcher.
313 You give CATCH 1 or 2 parameters.
314 The first 1st parameter is the "code to execute"
315 The 2nd (optional) is where to put the error message.
317 CATCH returns 0 on success, 1 for failure.
318 The "![catch command]" is self explaintory.
321 The 3rd parameter to IF must be exactly "else" or "elseif" [I lied
322 above, the IF command can take many parameters they just have to
323 be joined by exactly the words "else" or "elseif".
325 The 4th parameter contains:
327 "error [format STRING....]"
329 This lets me modify the previous lower level error by tacking more
330 text onto the end of it. In this case, i want to add the MMR register
331 name to make my error message look better.
334 Back to something inside show_mmr32_reg{}.
336 You'll see something 'set fn show_${NAME}_helper' Here I am
337 constructing a 'function name' Then - I look it up to see if it
338 exists. {the function: "proc_exists" does this}
340 And - if it does - I call the function.
342 In "C" it is alot like using: 'sprintf()' to construct a function name
343 string, then using "dlopen()" and "dlsym()" to look it up - and get a
344 function pointer - and calling the function pointer.
346 In this case - I execute a dynamic command. You can do some cool
347 tricks with interpretors.
351 Function: show_mmr32_bits()
353 In this case, we use the special TCL command "upvar" which tcl's way
354 of passing things by reference. In this case, we want to reach up into
355 the callers lexical scope and find the array named "NAMES"
357 The rest of the function is pretty straight forward.
359 First - we figure out the longest name.
360 Then print 4 rows of 8bits - with names.
363 **************************************************
364 ***************************************************
366 Open the file: "chips/atmel/at91/usarts.tcl"
369 First - about the AT91SAM series - all of the usarts
370 are basically identical...
372 Second - there can be many of them.
374 In this case - I do some more TCL tricks to dynamically
375 create functions out of thin air.
379 The "CHIP" file has defined some variables in a proper form.
381 ie: AT91C_BASE_US0 - for usart0,
382 AT91C_BASE_US1 - for usart1
385 Near the end of the file - look for a large "foreach" loop that
388 foreach WHO { US0 US1 US2 US3 US4 .... } {
392 In this case, I'm trying to figure out what USARTs exist.
394 Step 1 - is to determine if the NAME has been defined.
395 ie: Does AT91C_BASE_USx - where X is some number exist?
397 The "info exists VARNAME" tells you if the variable exists. Then -
398 inside the IF statement... There is another loop. This loop is the
399 name of various "sub-registers" within the USART.
401 Some more trick are played with the [set VAR] backtick evaluation stuff.
402 And we create two variables
404 We calculate and create the global variable name for every subregister in the USART.
405 And - declare that variable as GLOBAL so the world can find it.
407 Then - we dynamically create a function - based on the register name.
409 Look carefully at how that is done. You'll notice the FUNCTION BODY is
410 a string - not something in {braces}. Why? This is because we need TCL
411 to evaluate the contents of that string "*NOW*" - when $vn exists not
412 later, when the function "show_FOO" is invoked.
414 Lastly - we build a "str" of commands - and create a single function -
415 with the generated list of commands for the entire USART.
417 With that little bit of code - I now have a bunch of functions like:
419 show_US0, show_US1, show_US2, .... etc ...
421 And show_US0_MR, show_US0_IMR ... etc...
423 And - I have this for every USART... without having to create tons of
424 boiler plate yucky code.
426 ****************************************
427 ****************************************
428 END of the Tcl Intro and Walk Through
429 ****************************************
430 ****************************************
434 Some "GPIO" functions...