From c46dc5ba2dd1e21fd1936421114cfeac6d9d2cfb Mon Sep 17 00:00:00 2001 From: zwelch Date: Wed, 20 May 2009 08:48:19 +0000 Subject: Move TCL overview from source tree to doxygen manual. git-svn-id: svn://svn.berlios.de/openocd/trunk@1855 b42882b7-edfa-0310-969c-e2dbd0fdcd60 --- doc/manual/primer/tcl.txt | 438 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 doc/manual/primer/tcl.txt (limited to 'doc/manual') diff --git a/doc/manual/primer/tcl.txt b/doc/manual/primer/tcl.txt new file mode 100644 index 00000000..f2c3d053 --- /dev/null +++ b/doc/manual/primer/tcl.txt @@ -0,0 +1,438 @@ +/** @page primertcl OpenOCD TCL Primer + +@verbatim + +**************************************** +**************************************** + +This is a short introduction to 'un-scare' you about the language +known as TCL. It is structured as a guided tour through the files +written by me [Duane Ellis] - in early July 2008 for OpenOCD. + +Which uses the "JIM" embedded Tcl clone-ish language. + +Thing described here are *totally* TCL generic... not Jim specific. + +The goal of this document is to encourage you to add your own set of +chips to the TCL package - and most importantly you should know where +you should put them - so they end up in an organized way. + +--Duane Ellis. + duane@duaneellis.com + +**************************************** +**************************************** + +Adding "chip" support - Duane Ellis July 5 - 2008. + +The concept is this: + In your "openocd.cfg" file add something like this: + + source [find tcl/chip/VENDOR/FAMILY/NAME.tcl] + + For example... + source [find tcl/chip/atmel/at91/at91sam7x256.tcl] + + You'll notice that it makes use of: + + tcl/cpu/arm/.tcl. + + Yes, that is where you should put "core" specific things. + Be careful and learn the difference: + + THE "CORE" - is not the entire chip! + +Definition: + That "file" listed above is called a "CHIP FILE". + + It may be standalone, or may need to "source" other "helper" files. + + The reference [7/5/2008] is the at91sam7x256.tcl file. + +**************************************** +**************************************** +=== TCL TOUR === +Open: at91sam7x256.tcl +=== TCL TOUR === + +A walk through --- For those who are new to TCL. + +Examine the file: at91sam7x256.tcl + +It starts with: + source [find path/filename.tcl] + +In TCL - this is very important. + + Rule #1 Everything is a string. + Rule #2 If you think other wise See #1. +Reminds you of: + Rule #1: The wife is correct. + Rule #2: If you think otherwise, See #1 + +Any text contained inside of [square-brackets] +is just like `back-ticks` in BASH. + +Hence, the [find FILENAME] executes the command find with a single +parameter the filename. + +======================================== + +Next you see a series of: + +set NAME VALUE + +It is mostly "obvious" what is going on. + +Exception: The arrays. + + You would *THINK* Tcl supports arrays. + In fact, multi-dim arrays. That is false. + + For the index for"FLASH(0,CHIPSELECT)" is actually the string + "0,CHIPSELECT". This is problematic. In the normal world, you think + of array indexes as integers. + + For example these are different: + + set foo(0x0c) 123 + set foo(12) 444 + + Why? Because 0x0c {lowercase} is a string. + Don't forget UPPER CASE. + + You must be careful - always... always... use simple decimal + numbers. When in doubt use 'expr' the evaluator. These are all the + same. + + set x 0x0c + set foo([expr $x]) "twelve" + + set x 12 + set foo([expr $x]) "twelve" + + set x "2 * 6" + set foo([expr $x]) "twelve" + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "bitsbytes.tcl" + +There is some tricky things going on. +=============== + +First, there is a "for" loop - at level 0 +{level 0 means: out side of a proc/function} + +This means it is evaluated when the file is parsed. + +== SIDEBAR: About The FOR command == +In TCL, "FOR" is a funny thing, it is not what you think it is. + +Syntactically - FOR is a just a command, it is not language +construct like for(;;) in C... + +The "for" command takes 4 parameters. + (1) The "initial command" to execute. + (2) the test "expression" + (3) the "next command" + (4) the "body command" of the FOR loop. + +Notice I used the words "command" and "expression" above. + +The FOR command: +1) executes the "initial command" +2) evaluates the expression if 0 it stops. +3) executes the "body command" +4) executes the "next command" +5) Goto Step 2. + +As show, each of these items are in {curly-braces}. This means they +are passed as they are - KEY-POINT: un evaluated to the FOR +command. Think of it like escaping the backticks in Bash so that the +"under-lying" command can evaluate the contents. In this case, the FOR +COMMAND. + +== END: SIDEBAR: About The FOR command == + +You'll see two lines: + +LINE1: + set vn [format "BIT%d" $x] + +Format is like "sprintf". Because of the [brackets], it becomes what +you think. But here's how: + +First - the line is parsed - for {braces}. In this case, there are +none. The, the parser looks for [brackets] and finds them. The, +parser then evaluates the contents of the [brackets], and replaces +them. It is alot this bash statement. + + EXPORT vn=`date` + +LINE 2 & 3 + set $vn [expr (1024 * $x)] + global $vn + +In line 1, we dynamically created a variable name. Here, we are +assigning it a value. Lastly Line 3 we force the variable to be +global, not "local" the the "for command body" + +=============== +The PROCS + +proc create_mask { MSB LSB } { + ... body .... +} + +Like "for" - PROC is really just a command that takes 3 parameters. +The (1) NAME of the function, a (2) LIST of parameters, and a (3) BODY + +Again, this is at "level 0" so it is a global function. (Yes, TCL +supports local functions, you put them inside of a function} + +You'll see in some cases, I nest [brackets] alot and in others I'm +lazy or wanted it to be more clear... it is a matter of choice. +=============== + + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "memory.tcl" +=============== + +Here is where I setup some 'memory definitions' that various targets can use. + +For example - there is an "unknown" memory region. + +All memory regions must have 2 things: + + (1) N_ + (2) NAME( array ) + And the array must have some specific names: + ( , THING ) + Where: THING is one of: + CHIPSELECT + BASE + LEN + HUMAN + TYPE + RWX - the access ability. + WIDTH - the accessible width. + + ie: Some regions of memory are not 'word' + accessible. + +The function "address_info" - given an address should +tell you about the address. + + [as of this writing: 7/5/2008 I have done + only a little bit with this -Duane] + +=== +MAJOR FUNCTION: +== + +proc memread32 { ADDR } +proc memread16 { ADDR } +proc memread8 { ADDR } + +All read memory - and return the contents. + +[ FIXME: 7/5/2008 - I need to create "memwrite" functions] + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "mmr_helpers.tcl" +=============== + +This file is used to display and work with "memory mapped registers" + +For example - 'show_mmr32_reg' is given the NAME of the register to +display. The assumption is - the NAME is a global variable holding the +address of that MMR. + +The code does some tricks. The [set [set NAME]] is the TCL way +of doing double variable interpolation - like makefiles... + +In a makefile or shell script you may have seen this: + + FOO_linux = "Penguins rule" + FOO_winXP = "Broken Glass" + FOO_mac = "I like cat names" + + # Pick one + BUILD = linux + #BUILD = winXP + #BUILD = mac + FOO = ${FOO_${BUILD}} + +The "double [set] square bracket" thing is the TCL way, nothing more. + +---- + +The IF statement - and "CATCH" . + +Notice this IF COMMAND - (not statement) is like this: +[7/5/2008 it is this way] + + if ![catch { command } msg ] { + ...something... + } else { + error [format string...] + } + +The "IF" command expects either 2 params, or 4 params. + + === Sidebar: About "commands" === + + Take a look at the internals of "jim.c" + Look for the function: Jim_IfCoreCommand() + And all those other "CoreCommands" + + You'll notice - they all have "argc" and "argv" + + Yea, the entire thing is done that way. + + IF is a command. SO is "FOR" and "WHILE" and "DO" and the + others. That is why I keep using the phase it is a "command" + + === END: Sidebar: About "commands" === + +Parameter 1 to the IF command is expected to be an expression. + +As such, I do not need to wrap it in {braces}. + +In this case, the "expression" is the result of the "CATCH" command. + +CATCH - is an error catcher. + +You give CATCH 1 or 2 parameters. + The first 1st parameter is the "code to execute" + The 2nd (optional) is where to put the error message. + + CATCH returns 0 on success, 1 for failure. + The "![catch command]" is self explaintory. + + +The 3rd parameter to IF must be exactly "else" or "elseif" [I lied +above, the IF command can take many parameters they just have to +be joined by exactly the words "else" or "elseif". + +The 4th parameter contains: + + "error [format STRING....]" + +This lets me modify the previous lower level error by tacking more +text onto the end of it. In this case, i want to add the MMR register +name to make my error message look better. + +--------- +Back to something inside show_mmr32_reg{}. + +You'll see something 'set fn show_${NAME}_helper' Here I am +constructing a 'function name' Then - I look it up to see if it +exists. {the function: "proc_exists" does this} + +And - if it does - I call the function. + +In "C" it is alot like using: 'sprintf()' to construct a function name +string, then using "dlopen()" and "dlsym()" to look it up - and get a +function pointer - and calling the function pointer. + +In this case - I execute a dynamic command. You can do some cool +tricks with interpretors. + +---------- + +Function: show_mmr32_bits() + +In this case, we use the special TCL command "upvar" which tcl's way +of passing things by reference. In this case, we want to reach up into +the callers lexical scope and find the array named "NAMES" + +The rest of the function is pretty straight forward. + +First - we figure out the longest name. +Then print 4 rows of 8bits - with names. + + +************************************************** +*************************************************** +=== TCL TOUR === +Open the file: "chips/atmel/at91/usarts.tcl" +=============== + +First - about the AT91SAM series - all of the usarts +are basically identical... + +Second - there can be many of them. + +In this case - I do some more TCL tricks to dynamically +create functions out of thin air. + +Some assumptions: + +The "CHIP" file has defined some variables in a proper form. + +ie: AT91C_BASE_US0 - for usart0, + AT91C_BASE_US1 - for usart1 + ... And so on ... + +Near the end of the file - look for a large "foreach" loop that +looks like this: + + foreach WHO { US0 US1 US2 US3 US4 .... } { + + } + +In this case, I'm trying to figure out what USARTs exist. + +Step 1 - is to determine if the NAME has been defined. +ie: Does AT91C_BASE_USx - where X is some number exist? + +The "info exists VARNAME" tells you if the variable exists. Then - +inside the IF statement... There is another loop. This loop is the +name of various "sub-registers" within the USART. + +Some more trick are played with the [set VAR] backtick evaluation stuff. +And we create two variables + +We calculate and create the global variable name for every subregister in the USART. +And - declare that variable as GLOBAL so the world can find it. + +Then - we dynamically create a function - based on the register name. + +Look carefully at how that is done. You'll notice the FUNCTION BODY is +a string - not something in {braces}. Why? This is because we need TCL +to evaluate the contents of that string "*NOW*" - when $vn exists not +later, when the function "show_FOO" is invoked. + +Lastly - we build a "str" of commands - and create a single function - +with the generated list of commands for the entire USART. + +With that little bit of code - I now have a bunch of functions like: + + show_US0, show_US1, show_US2, .... etc ... + + And show_US0_MR, show_US0_IMR ... etc... + +And - I have this for every USART... without having to create tons of +boiler plate yucky code. + +**************************************** +**************************************** +END of the Tcl Intro and Walk Through +**************************************** +**************************************** + +FUTURE PLANS + + Some "GPIO" functions... + +@endverbatim + + */ -- cgit v1.2.3