The Way of the great learning involves manifesting virtue, renovating the people, and abiding by the highest good.

2009年1月5日星期一

JTAGER

JTAGER User Manual

Version History:

- 0.2.0, 2004-09-25

- 0.1.0, 2003-11-30

Copyright (C) 2003 2004, Rongkai Zhan (Chinese Name: 詹荣开)


Table of Contents

1 Introduction

1.1 What is JTAGER

1.2 The List of Devices Supported

Build and Configuration

The Commands Set of JTAGER

3.1 Command Syntax Rules

3.2 Commands Group

3.3 Reset Command

3.4 Help Command

3.5 Idcode Command

3.6 Exit/quit Commands

3.7 Halt Command

3.8 Restart Command

3.9 Ice Command

3.10 Reg Command

3.11 Execbat Command

3.12 Memcmp Command

3.13 Memcpy Command
3.14 Memcsum Command

3.15 Memdump Command

3.16 Memset Command

3.17 Memtest Command

3.18 Flprobe Command

3.19 Flerase Command

3.20 Flwrite Command


Chapter 1 Introduction

1.1 What is JTAGER

JTAGER is a simple embedded system debugger running on the Linux host. By JTAGER, we can: 1) read or write the core registers of the CPU on the embedded target board. 2) Read or write the external device registers of the target board. 3) Read or write the RAM on the target board. 4) Read or write the solid storage device on the target board, such as Flash.


+------------+         +--------------------+         +--------------+
|            |         |                    |         |              |
|            |         |                    |         |              |
| Debug Host | <-----> | Protocol Converter | <-----> | Debug Target |
|            |         |                    |         |              |
|            |         |                    |         |              |
+------------+         +--------------------+         +--------------+
      |                           |
      |                           |
      V                           V
The host running         For example, Multi-ICE,
the debug toolkits       Wiggler-compatible JTAG
(such JTAGER)             dongle, etc.

Figure 1 A typical embedded debug system

 

To debug the embedded target board, we need to use a protocol converter based on JTAG interface to transfer signal and data between the host side and the target side. Figure 1 can demonstrate a typical embedded debug system.

 

Because it is very simple and very cheap, the MacriagorWiggler-compatible JTAG dongles are used most frequently. The circuit diagram can be found here:

 

Simple JTAG interface circuit. 
============================== 
 
PL1 25wayD Male                                                 PL2 20wayIDC 
 
PL1/17-25 <--------------+-------------------------+--+-----------<  PL2/4,6,8, 
                         |                         |  |              10,12,14, 
                         |    AC244          200nF =  = 4.7uF        16,18,20 
                         | +------------+  Vcc     |  | 
         TDI          0v +-| 1       20 |-+--------+--+-----------<  PL2/1,2 
PL1/5  >-------------------| 2       19 |-+ 
         TMS               | 3       18 |-----XXXX---------------->  PL2/5 
PL1/3  >-------------------| 4       17 |     51R      
         TCLK              | 5       16 |-----XXXX---------------->  PL2/7 
PL1/4  >-------------------| 6       15 |     51R 
                           | 7       14 |-----XXXX---------------->  PL2/9 
                      +----| 8       13 |     51R 
                      |    | 9       12 |-----XXXX---+ 
                      |  +-| 10      11 |     51R    | 
                      |  | +------------+            | 
                      |  V 0v                        | 
                      +------------------------------------------<   PL2/13 
         TDO                                         | 
PL1/11 <---------------------------------------------+ 
 
 
                            DTC114   /-------xxxx----------------<   PL2/15 
         RST            10k       | /        51R 
PL1/2  >----------------XXXX--+---|< 
                              |   | \ 
                              X      V 
                         47k  X      |   
                              X      | 
                              |      | 
                              V 0v   V 

 


1.2 The List of Devices Supported

(1) The types of target CPU core

  • ARM7TDMI/ARM720T core.
  • ARM9TDMI/ARM920T core.

 

(2) The types of JTAG interfaces

  • Macriagor Wiggler JTAG dongle

 

(3) The types of FLASH chips

  • SST39LF/VF160
  • MBM29LV650.

 


Chapter 2 Build and Configuration

Because the root privilege is needed to install and run JTAGER, so we assume that you have get the root privilege of your Linux host. If not, please contact with the administrator.

 

First, download the JTAGER package and place it in some directory, such as the “/tmp” directory. Follow the following steps to unpack it.

 

#cd /tmp

#tar zxvf jtager-0.1.0.tar.gz

#cd jtager-0.1.0

 

Then, run configure script and compile:

 

#configure --prefix=/usr/local/jtager

#make all

 

Finally, run “make install” to install JTAGER to your specified path (the default path is /usr/local):

 

#make install

 

To run JTAGER, type “./jtager” and press :

 

#cd /usr/local/jtager/bin

#./jtager


Chapter 3 The Commands Set of JTAGER

JTAGER is a program driven by commands. When users run JTAGER program in shell, JTAGER will print its command prompt – “JTAGER:>”, after outputting some logo informations.

3.1 Command Syntax Rules

All JTAGER commands, command options and arguments are case-insensitive, and separated by the space character.

 

All JTAGER commands use the GNU-style long-named options. That is, each JTAGER command option is leaded by two consecutive chars ‘-‘. For example:

 

JTAGER:> ice --list

 

JTAGER command options may be followed its option argument, for example:

 

JTAGER:> memdump --base0x01c80000

 

JTAGER commands also maybe have non-option arguments. For example, in the following JTAGER command, “0x111” is a non-option argument:

 

JTAGER:> reg --name=r0 0x111

 

*) The rule for JTAGER command arguments representation

 

Any arguments are a string or an integer. For an integer argument, it can use the hexadecimal representation or the decimal representation. For example, these hex representations  “0xff0 0Xff0 ff0H FF0h” can all be accepted by JTAGER. If not explicitly using hex representation like these, the arguments are treated as decimal number by default. All option arguments and non-option arguments of each JTAGER command comply with this rule.

 

*) The rule for memory block size representation

 

This rule is for the '--size' option, which is used to specify the memory block size. Anyhow, the argument of the '--size' option is sure to obey the rule for command arguments representation (see 1.1). To specify how many bytes (8-bit), how many half-words (16-bit), or how many words (32-bits), we can append a suffix character 'b' 'B' 's' 'S' 'w' or 'W' to the option argument of the '--size' option, and we call the suffix character "the size indicator suffix". For example:

 

        JTAGER:> memdump 0x01c80000 --size=0xfb

   or

        JTAGER:> memdump 0x01c80000 --size=0xfB

 

The above commands will dump the contents of the memory block [0x01C800000, 0x01C80000F], whose size is 0xF bytes.

 

ATTENTION PLEASE: Because the character 'b' or 'B' is also a hexadeciamal char, '--size=0xfb' doesn't means the memory block size is 251 (=0xFB) bytes. Therefore, if you want to specify the memory block size is 251 bytes, you should use '--size=0xFBB'.

 

To specify how many half-word the memory block is, we can use the following commands:

 

        JTAGER:> memdump 0x01c80000 --size=0xfs

    or

        JTAGER:> memdump 0x01c80000 --size=0xfS

 

NOTE: The suffix 's' or 'S' means short.

 

If there is no suffix character, we means how many words by default. For example, the following command will dump the memory block [0x01C00000, 0x01C0003C], whose size is 15 words:

 

        JTAGER:> memdump 0x01c80000 --size=0xf

 

Of course, you also can explicitly specify that its size is 15 words:

 

        JTAGER:> memdump 0x01c80000 --size=0xfw

   or

        JTAGER:> memdump 0x01c80000 --size=0xfW

 

NOTE: All '--size' options obey this rule. And the '--size' option has a default value: 1 word.


3.2 Commands Group

To more exactly express the function of each JTAGER command, we separate all JTAGER commands into two groups: 1) the base commands group. 2) The debug commands group.

 

The basic commands group contains five JTAGER commands:

  • reset command: This command resets the JTAG interface, and brings the TAP controller of the target CPU into the Run-Test/Idle state.
  • help command: It shows the help information of some specified JTAGER command, or prints a list of all JTAGER commands.
  • idcode command: This command reads the IDCODE value of your target CPU.
  • exit/quit commands: exits JTAGER program. They are equivalent.

 

The debug commands group contains the following commands:

 

  • halt command: This command sends the DEBUG request to the target CPU. If success, the target will stop its current execution path and enter into the debug state.
  • restart command: This command ends the debug state of the target, and restarts the target CPU from the last point halted by JTAGER halt command. After this command is executed successfully, the target will be the normal system state.
  • ice command: It can read or write the current values of the EmbeddedICE-RT logic registers within your target CPU.
  • reg command: It can read or write the core registers of your target CPU.
  • execbat command: Execute a JTAGER batch command file
  • memcmp command: Compare two memory blocks.
  • memcpy command: Copy one memory block to another memory block.
  • memcsum command: Calculate the MD5 sum of the memory block.
  • memdump command: Display the content of the memory block
  • memset command: Modify the content of the memory block
  • memtest command: Test if the memory block is fully read-able and write-able.
  • flprobe command: Probe flash chips at the specified base address.
  • flerase command: Erase one FLASH sector or the whole chip
  • flwrite command: Write data into Flash memory

3.3 Reset Command

The JTAGER reset command can be used to reset the JTAG link between your host side and your target side, and bring the TAP controller of your target CPU into the Run-Test/Idle state. You can refer to IEEE Standard 1149.1 – 1990 <> for more information about the TAP controller state machine.

 

This command has no any options or arguments. Command syntax:

 

reset
















3.4 Help Command

The JTAGER help command can show the help information of some specified JTAGER command, or print a list of all available JTAGER commands.

 

Command syntax:

 

help [[--cmdname=]cmdname]

 

The option argument cmdname is optional, and can be any legal JTAGER command name string. If the argument cmdname is not specified, the help command will print a list of all JTAGER commands. The list contains into two columns, which are separated by the tab character ‘\t’. The first column is command name string; the second one is the command description string.

 

For example, you can type “help help” to get the help information about the help command:

 

USAGE:

       help [[--cmdname=]cmdname]

 

DESCRIPTION:

       Show the list of all available JTAGER commands, or show the

verbose information of the specified JTAGER command.

 

PARAMETERS:

       --cmdname: The spcified JTAGER command name.


3.5 Idcode Command

The JTAGER idcode command is used to read the IDCODE value of your target CPU. Generally speaking, each embedded CPU with the support of JTAG test has a unique 32-bit IDCODE value. According to the JEDEC standard and <>, the bit[0] of IDCODE value must always be 1, its bit[11 : 1] represents the unique manufacturer ID, its bit[27 : 12] represents the product ID number, and its bit[31 : 28] represents version number. Each kind of CPU has different IDCODE value. Refer to the BSDL file of your target CPU to get its specific IDCODE value.

 

The JTAGER idcode command has no any command options or arguments. Its command syntax:

 

idcode

 

Another use of the JTAGER idcode command is to check whether the link between host, JTAG emulator and target is ok. Before using the halt command to enter the debug state, user had better use the idcode command to read back the IDCODE value of your target. If the value we read back is equal to the specific IDCODE value of your target CPU, it seems that everything is ok J

 

For example, for author’s test board that is based on Samsung S3C44B0X CPU, the call to idcode command will print the following information:

 

Reading the JTAG Device ID code ... [OK]

Device ID = 0x1F0F0F0F

       bit[0] = 1, always be 1, required by IEEE Std 1149.1

       Manufacturer (bit[11:1]): 0x787 – SAMSUNG

       Part number (bit[27:12]): 0xF0F0

       Version (bit[31:28])   : 0x1

 

The value 0x1F0F0F0F read back is just the one the author expects for his Samsung S3C44B0X CPU.


3.6 Exit/quit Commands

Both the JTAGER exit command and the quit command can be used to exit JTAGER program. They are equivalent. Their command syntax:

 

exit or quit


3.7 Halt Command

The JTAGER halt command can make your target CPU enter into the debug state by sending DEBUG request to your target CPU. After receiving the DEBUG request, the target CPU will stop its current execution path and enter the debug state. When in the debug state, the target CPU is not driven by its internal clock pulse signal, and is fully controlled by the debug clock pulse signal from user’s host side.

 

The JTAGER halt command has no any options or arguments. Its command syntax:

 

halt

 

The JTAGER halt command is the bases of the other JTAGER debug commands (such as reg, mem and flash commands). Only if the target is halted into the debug state, the other debug commands can be used, otherwise they will print an error message like this:

 

Error: The target is running. Please use "halt" command to halt it.


3.8 Restart Command

The JTAGER restart command can end user’s current debug session created by the last call to the halt command, and make the target CPU return to the normal system state, that is, the target CPU resumes its execution from the last halted point.

 

Command syntax:

 

restart -–pc=address


3.9 Ice Command

The JTAGER ice command can read or write the contents of the EmbeddedICE-RT logic’s registers in your target CPU.

 

Command syntax:

 

ice [--list]

ice --name=regname [[--value=]newvalue]

 

When the command is called with no any options and arguments, it will read and print the contents of all ICE registers.

 

--list

The option is used to list all legal ICE registers short names. It is optional.

 

--name=regname

The option is used to specify the name of the register to read or write. Its option argument regnamemust be one of the names listed by the --list option.

 

--value=newvalue

The option --value represents the new value set for the specified ICE register. It is optional. If it is specified, then the specified new value will be written into the specified ICE register. Otherwise, the ice command will read the current value of the specified ICE register.


3.10 Reg Command

The JTAGER reg command can read or write the core registers of the target CPU. NOTE: this command must be used only if the target CPU is in the debug state.

 

The command syntax of this command is similar with the one of the JTAGER ice command. Its command syntax:

 

reg [--list]

reg --name=regname [[--value=]newvalue]

 

When the command is called with no any options and arguments, it will read and print the current contents of all core registers of your target CPU.

 

--list

The option is used to list all legal CPU core registers names. It is optional.

 

--name=regname

The option is used to specify the name of the register to read or write. Its option argument regname must be one of the CPU core register names listed by the --list option.

 

--value=newvalue

The option --value represents the new value set for the specified core register. It is optional. If it is specified, then the specified new value will be written into the specified CPU core register. Otherwise, the reg command will read the current value of the specified CPU core register.


3.11 Execbat Command

DESCRIPTION: Execute a jtager batch-command file
     SYNTAX: execbat /your/jtager/batch/file


A jtager batch command file is a file containing a sequence of jtager commands. The main purposal of a jtager batch command file is that we can write some jtager commands to initialize a raw target board. In a raw target board, the ram space is usually not available, and the UART is also not available too. Therefore, we can put some jtager commands used to setup the memory controller or UART port in a jtager batch command file.


In the source tarball of jtager, the "extra/s3c44b0x-init.bat" is a jtager batch command file for initializing my S3C44B0X develop board. When you write yourself jtager batch command file, you can use this file as a template.


For example, the following command will initialize my S3C44B0X board:


    JTAGER:> execbat /root/jtager/extra/s3c44b0x-init.bat
    *** line 12: reg --name=cpsr --value=0x000000D3
    CPSR register has been set new value 0x000000D3, successfully.
    *** line 17: memset 0x01D30000 --value=0x0
    *** line 22: memset 0x01d80000 --value=0x34021
    *** line 23: memset 0x01d80004 --value=0x7ff8
    *** line 24: memset 0x01d80008 --value=0x09
    ......

3.12 Memcmp Command

DESCRIPTION: Copy memory block from one to another
     SYNTAX: memcpy --to=addr1 --from=addr2 [[--size=]count]

The option '--to' specifies the base address of the destination memory block, while the option '--from' specifies the base address of the source memory block. The option '--size' specifies the size of these two memory blocks.

For example, the following command will copy the contents of the memory block [0x0C100000, 0x0C101000) to the memory block [0x0C200000, 0x0C201000):

    JTAGER:> memcpy --to=0x0C200000 --from=0x0C100000 --size=0x1000b
    memcpy: [0x0C100000, 0x0C101000) => [0x0C200000, 0x0C201000) ... [OK]
    Total 4096 bytes are copied.

After executing the above command, we run 'memcmp' command again, and will find these two memory blocks are the same:

    JTAGER:> memcmp --base1=0x0C100000 --base2=0x0C200000 --size=0x1000b
    memcmp: [0x0C100000, 0x0C101000) =?= [0x0C200000, 0x0C201000) .... [YES]

3.13 Memcpy Command

DESCRIPTION: Copy memory block from one to another
     SYNTAX: memcpy --to=addr1 --from=addr2 [[--size=]count]

The option '--to' specifies the base address of the destination memory block, while the option '--from' specifies the base address of the source memory block. The option '--size' specifies the size of these two memory blocks.

For example, the following command will copy the contents of the memory block [0x0C100000, 0x0C101000) to the memory block [0x0C200000, 0x0C201000):

    JTAGER:> memcpy --to=0x0C200000 --from=0x0C100000 --size=0x1000b
    memcpy: [0x0C100000, 0x0C101000) => [0x0C200000, 0x0C201000) ... [OK]
    Total 4096 bytes are copied.

After executing the above command, we run 'memcmp' command again, and will find these two memory blocks are the same:

    JTAGER:> memcmp --base1=0x0C100000 --base2=0x0C200000 --size=0x1000b
    memcmp: [0x0C100000, 0x0C101000) =?= [0x0C200000, 0x0C201000) .... [YES]

3.14 Memcsum Command

DESCRIPTION: Calculate the MD5 sum of a memory block
     SYNTAX: memcsum/md5sum [--base=]addr [--size=count]
      ALIAS: md5sum

The option '--base' specifies the base address of the memory block, while the option '--size' specifies the size of the memory block.

For example, the following command will calculate the MD5 sum of the memory block [0x0C100000, 0x0C101000):

    JTAGER:> md5sum 0x0C100000 --size=0x1000b
    e95065d361b843a7025a63830273fde8  memory block [0x0C100000, 0x0C100FFF]

From the above output information, we know that the MD5 sum of memory block [0x0C100000, 0x0C101000) is:
e95065d361b843a7025a63830273fde8 

3.15 Memdump Command

DESCRIPTION: Display the contents of a memory block
     SYNTAX: memdump [--base=]addr [--size=count]

For example, the following command will dump the memory block [0x0C100000, 0x0C100040) by the byte-output way:

    JTAGER:> memdump 0x0C100000 --size=64b
    0x0C100000: FF FF FB F7 FF FF FF FE   FF BF F7 FF FF FD FF FF   |................|
    0x0C100010: FF FF FF FF FF FD FF FF   FF F5 FB FF FF FF FE FF   |................|
    0x0C100020: FF FF FF FF FF FD FF FF   FF FF FF FF FF FF FF FD   |................|
    0x0C100030: FF FF FF FF EF FF FF FF   FF FF FF FF FF FF FF FE   |................|

The following command will dump the memory block by the memory block [0x0C100000, 0x0C100040) by the half-word output way:

    JTAGER:> memdump 0x0C100000 --size=32s
    0x0C100000: FFFF F7FB FFFF FEFF   BFFF FFF7 FDFF FFFF   |................|
    0x0C100010: FFFF FFFF FDFF FFFF   F5FF FFFB FFFF FFFE   |................|
    0x0C100020: FFFF FFFF FDFF FFFF   FFFF FFFF FFFF FDFF   |................|
    0x0C100030: FFFF FFFF FFEF FFFF   FFFF FFFF FFFF FEFF   |................|

And The following command will dump the memory block by the memory block [0x0C100000, 0x0C100040) by the word-output way:

    JTAGER:> memdump 0x0C100000 --size=16
    0x0C100000: F7FBFFFF FEFFFFFF   FFF7BFFF FFFFFDFF   |................|
    0x0C100010: FFFFFFFF FFFFFDFF   FFFBF5FF FFFEFFFF   |................|
    0x0C100020: FFFFFFFF FFFFFDFF   FFFFFFFF FDFFFFFF   |................|
    0x0C100030: FFFFFFFF FFFFFFEF   FFFFFFFF FEFFFFFF   |................|

3.16 Memset Command

DESCRIPTION: Modify the content of a memory block
     SYNTAX: memset [--base=]addr
             memset [--base=]addr --size=count --value=newval
             memset [--base=]addr --infile=pathname

The command 'memset' has two work modes: 'one-by-one' mode and 'fill' mode.

3.16.1 one-by-one mode


If you run 'memset' command with only one '--base' option, then we are in one-by-one work mode. For example:

    JTAGER:> memset 0x0C100000
    press 'q' or 'Q' or 'ENTER' to exit.
    0x0C100000: F7FBFFFF ? 0xAA55AA55
    0x0C100000: AA55AA55
    0x0C100004: FEFFFFFF ? 0x55AA55AA
    0x0C100004: 55AA55AA
    0x0C100008: FFF7BFFF ? 1234565
    0x0C100008: 0012D685
    0x0C10000C: FFFFFDFF ?

When in the 'one-by-one' mode, the memset command can let the user modify the memory unit in an interactive way, and it will echo the new value.

When in the 'one-by-one' mode and only in the 'one-by-one' mode, we also can append "the size indicator suffix" to the arguement of the '--base' option. For example:

    JTAGER:> memset 0x0C100000s
    press 'q' or 'Q' or 'ENTER' to exit.
    0x0C100000: AA55 ? 255
    0x0C100000: 00FF
    0x0C100002: AA55 ? 100
    0x0C100002: 0064
    0x0C100004: 55AA ?

 or

    JTAGER:> memset 0x0C100000b
    press 'q' or 'Q' or 'ENTER' to exit.
    0x0C100000: FF ? 0x11
    0x0C100000: 11
    0x0C100001: 00 ? 0x22
    0x0C100001: 22
    0x0C100002: 64 ?

3.16.2 fill mode


When in the 'fill' mode, the memset command will fill the specified memory block with the specified new value. And the default argument of the '--value' option is zero. For example:

    JTAGER:> memset 0x0C100000 --size=0x1000b
    memset: The memory block [0x0C100000, 0x0C101000) is filled with new value: 0x0
    Total 4096 bytes are filled.

The above command will fill the memory block [0x0C100000, 0x0C101000) with zero. If the '--size' option is missed, then the memset command will only fill one word. For example, the following command will set the word 0x0C100000 with the new value 0xAA55AA55.
    
    JTAGER:> memset 0x0C100000 --value=0xAA55AA55

When in the 'fill' mode, the memset command also can fill the specified memory block with the data from a host binary file. This can be done by the '--infile' option. For example, the following command will fill the memory block whose base address is 0x0C100000, with the binary data from the host file "/root/aa.bin":

    JTAGER:> memset 0x0c100000 --infile=/root/aa.bin
    memset: Fill the memory block [0x0C100000, 0x0C100022] with the file "/root/aa.bin" .... [OK]
    Total 35 bytes are written.

After executing the above command, we can use the memcsum command to verify whether the data within the memory block [0x0C100000, 0x0C100022] is the same as the data in the host binary file "/root/aa.bin":

    JTAGER:> memcsum 0x0c100000 --size=35b
    memcsum: [0x0C100000, 0x0C100022] ... [OK]
    MD5 sum: 11a5cbd1592478e831067e96b529cd9a

In your host shell, use the md5sum system command to calculate the MD5 sum of the binary file "/root/aa.bin":

    [root@zhanrk root]# md5sum aa.bin
    11a5cbd1592478e831067e96b529cd9a  aa.bin

We can see their MD5 sums are the same!

3.17 Memtest Command

DESCRIPTION: Test if a memory block is fully read-able and write-able.
     SYNTAX: memtest [--base=]addr [--size=count]

For example, the following command will test if the memory block [0x0C100000, 0x0C101000) is fully read-able
and write-able:

    JTAGER:> memtest 0x0C100000 --size=0x1000b
    memtest: testing if the memory block [0xC100000, 0xC100FFF] are fully read-able and write-able ... [OK]

Because the memory block is a ram block, it is fully read-able and write-able, of course. 

3.18 Flprobe Command

DESCRIPTION: Probe flash chips at the specified base address.
     SYNTAX: flprobe [[--chip-base=]addr]
     
The option '--chip-base' is optional, its default argument is 0x00. For example, the following command will
probe flash chip at the base address 0x00:

    JTAGER:> flprobe
    Probing flash chip at 0x00000000 ... [found : sst39vf160]
    base address: 0x00000000
       chip size: 2048 KB
     sector size: 4 KB
       bus width: 16-bit

3.19 Flerase command

DESCRIPTION: Erase one FLASH sector or the whole chip.
     SYNTAX: flerase --whole-chip
             flerase [--sector-address=]addr
     
For example, the following command will erase the flash sector [0x101000, 0x102000):

    JTAGER:> flerase 0x101000
    Erasing sst39vf160 flash sector [0x00101000, 0x00102000) ...... [OK]


3.20 Flwrite command

DESCRIPTION: Write data into Flash memory.
     SYNTAX: flwrite --to=addr1 --from=addr2 [[--size=]count]
             flwrite --to=addr1 --infile=pathname

For example, the following command will write the data of the memory block [0x0C100000, 0x0C104000) into
the flash memory block [0x101000, 0x105000):

    JTAGER:> flwrite --to=0x101000 --from=0x0c100000 --size=0x4000b
    Erasing sst39vf160 flash sector [0x00101000, 0x00102000) ... [OK]
    Erasing sst39vf160 flash sector [0x00102000, 0x00103000) ... [OK]
    Erasing sst39vf160 flash sector [0x00103000, 0x00104000) ... [OK]
    Erasing sst39vf160 flash sector [0x00104000, 0x00105000) ... [OK]
    Writing sst39vf160 Flash memory [0x00101000, 0x00105000) ....... [OK]

To verify whether the flash write operation is correct, we can use the 'memcsum' to calcute theiry MD5 sum:

    JTAGER:> memcsum 0x101000 --size=0x4000b
    5542f242c43a19f15a88400f044346d6  memory block [0x00101000, 0x00104FFF]
    
    JTAGER:> memcsum 0x0c100000 --size=0x4000b
    5542f242c43a19f15a88400f044346d6  memory block [0x0C100000, 0x0C103FFF]

You can see their MD5 sums are the same, so the flash write operation is correct. 

Using the '--infile' option, you also can write the data from a host binary file into the specified flash memory block. For example, the following command will write the binary data from the host file "/root/aa.bin" into the specified flash memory block [0x00101000, 0x00101023):

    JTAGER:> flwrite --to=0x101000 --infile=/root/aa.bin
    Erasing sst39vf160 flash sector [0x00101000, 0x00102000) ... [OK]
    Writing sst39vf160 Flash memory [0x00101000, 0x00101023) ... [OK]
    Total 35 bytes are written.


JTAGER Internals

Version: 0.1.0

November 2003

Rongkai Zhan (Chinese Name: 詹荣开)


Table of Contents

Ÿ       1 Overview

Ÿ       2 The JTAGER Commands Parser

o       2.1 The Data Struct of JTAGER Commands

o       2.2 The Commands Dispatcher

o       2.3 The Run Function of JTAGER Commands

Ÿ       3 The JTAG Interface Module

Ÿ       The Target CPU Cores Module

o       4.1 The Data Structs

Ÿ       5 The Flash Interface Module

o       5.1 The Flash Interface

o       5.2 How to Add the Support to A New Flash Chip

Ÿ       Index

Chapter 1 Overview

JTAGER is a program driven by commands, which is composed of four modules: 1) the commands parser module; 2) The JTAG emulators interface module; 3) The target CPU cores interface module; 4) The flash interface module. The figure 1 can demonstrate the relationship of these four modules.

 

+----------------------------------------------+

|              JTAGER Commands Parser          |

+----------------------------------------------+

              |                |           |

             \|/               |           |

+------------------------+     |           |

| Flash Interface Module |     |           |

+------------------------+     |           |

                    |          |           |

                   \|/        \|/          |

+-------------------------------------+    |

|  Target CPU Cores Interface Module  |    |

+-------------------------------------+    |

                           |               |

                          \|/             \|/

+----------------------------------------------+

|       JTAG Emulators Interface Module        |

+----------------------------------------------+

 

Figure 1 The relationships of the four JTAGER modules

 

Chapter 2 JTAGER Commands Parser

All source files of the JTAGER commands parser is placed in the directory src/cmd. The JTAGER commands parser is composed of two parts: 1) the unified commands parser interface; 2) the specified JTAGER commands parsing process. The unified commands parser is also composed of two parts: a jump pointer table and a commands dispatcher. The commands dispatcher is the only entry point of the JTAGER commands parser.

2.1 The Data Struct of JTAGER Commands

The data struct jtager_cmd_t is used to describe a JTAGER command, it is defined in the header file include/jtager.h:

 

typedef struct jtager_cmd_struct {

   const char *name;

   const char *description;

   struct option *long_options; /* the GNU-style long options */

   int non_option_args; /* the number of non-option arguments */

 

   void (*help) (void);

   int (*run) (int cmd_argc, char *cmd_argv[]);

}jtager_cmd_t;

 

List 1 The definition of jtager_cmd_t

 

The meanings of all members of the data struct:

 

o       name: A string pointer, which points to the name string of the JTAGER command. Each JTAGER command must have an unique name string.

 

o       descriptionThe simple description string of the JTAGER command.

 

o       long_options: The command options pointer, which points a command options array of the JTAGER command. NOTE: the last array element must be {0, 0, 0, 0}.

 

o       non_option_args: The number of the non-option arguments of the JTAGER command.

 

o       help: A function pointer, which points the help function of the JTAGER command.

 

o       run: A function pointer, which points the run function of the JTAGER command. At last, the commands dispatcher will call the run function of some specified JTAGER command by this function pointer.

 

Each JTAGER command has a variable of jtager_cmd_t struct type to describe itself, and its address is registered into the pointer array jtager_cmds[]. Therefore, we can jump to any JTAGER command that we wanted by the pointer array jtager_cmds[]. The function command_index(), which is implemented in the source file src/cmd/command.c, and the macrocmd_index() can help us to search the pointer array jtager_cmds[] by the command name string.

2.2 The Commands Dispatcher

The command dispatcher – the function parse_cmdline(), is implemented in the source file src/cmd/command.c. It is the only one entry point of the JTAGER command parser. At the same time it is also the overall control function of the whole command parsing processes, it will go down to the run function of the specified JTAGER command by the command name string. It is because of this that the function parse_cmdline() is called the commands dispatcher.

 

The commands parser contains four steps:

 

o       Step 1: Divide current command line string into many sub-strings.

 

Using the space character as the separator, this step will divide the whole command line string into many sub-strings, and save their addresses into the pointer array cmdline_argc[], at the same time the number of the sub-strings is saved in the variable cmdline_argc. NOTE: the array element cmdline_argv[0] will always point to the JTAGER command name string in the command line string, so the value of the variable cmdline_argc will always be greater than 1, while the array element cmdline_argv[cmdline_argc] will always be set to NULL.

 

The purpose of this step is simulating the argument argc and the argument argv of the main() function of C language, which can make the run function of the JTAGER commands to call the glibc function getopt_long() to parse the JTAGER commands options and arguments.

 

o       Step 2: Search the specified JTAGER command

 

As you know, the string pointer cmdline_argv[0] is always pointing to the JTAGER command name in the command line. Therefore, we can use it as the argument to call the macro cmd_index()to search in the pointer array jtager_cmds[].

 

o       Step 3: Check the legality of the current JTAGER command options and arguments

 

This step calls the function match_option_args() to check whether the command options and arguments in the current command line are just ones we wanted.

 

The reason that this step is needed is because there is a nasty bug for the glibc function getopt_long(). For example, we assume that there is a hello program, which can receive a long name option--list, and whose source codes are given by the List 2:

 

#include

#include

#include

static int list_flag = 0;

int option_index = 0;

struct option long_options[] = {

   {"list", no_argument, &list_flag, 1},

   {0, 0, 0, 0}

};

 

void main(int argc, char **argv)

{

   int retval;

  

   while (1) {

      retval = getopt_long(argc, argv, "l",

             long_options, &option_index);

      if (retval == -1)

          break;

      switch (retval) {

      case 0: break;

      case 'l': break;

      case '?':

      default:

          printf("Error: syntax error.\n");

          exit(-1);

      }

   }

   if (list_flag)

      printf("The --list option is specified.\n");

   exit(0);

}

 

List 2 The source codes of hello.c

 

For this hello program, if we execute it by the command line “hello --li 0x11”, it will still print the message “The --list option is specified.”, and it can recognize the illegal non-option argument 0x11, hoho!

 

o       Step 4: Call the run function of the specified JTAGER command

 

By calling the run function of the specified JTAGER command, the command dispatcher will pass the control to the run function of the specified JTAGER command, and the run function of the specified JTAGER command will parse the current command line further.

2.3 The Run Function of JTAGER Commands

The run function of the specified JTAGER command is the last process of the JTAGER command parser. Its main tasks are: 1) calling the glibc function getopt_long() to parse its command options, option arguments and non-option arguments; 2) Executing the different operations by the options specified by users, which generally will call the JTAGER flash interface, the JTAGER target CPU cores interface and the JTAGER jtag interface.

Chapter 3 The JTAG Interface Module

(To be added)

Chapter 4 The Target CPU Cores Module

All source files of the CPU cores interface module implementation are placed in the directory src/target and its sub-directories. The source files in the sub-directory src/target/arm7tdmiimplement the ARM7TDMI core, while the source files in the sub-directory src/target/arm9tdmi implement the ARM9TDMI core.

4.1 The Data Structs

The data struct core_register_t is used to describe a CPU core register, for example, the R0 register and CPSR register in the ARM core. This data struct is defined in the header fileinclude/target.h:

 

typedef struct core_register_struct {

   char *name;

   u32 value;

}core_register_t;

 

List 4-1 The definition of the data struct core_register_t

 

The struct member name represents the name of the CPU core register, and the struct member value represents the current value of the CPU core register.

 

The data struct ice_register_t is used to describe the ICE registers of the EmbeddedICE-RT logic in the ARM7TDMI core or ARM9TDMI core. It is defined in the header fileinclude/target.h:

 

typedef struct ice_register_struct {

   char *name;

   char *desc; /* long name string */

   u32 addr;

   int bitnr; /* the bit length of register */

   u32 value;

}ice_register_t;

 

List 4-2 The definition of the data struct ice_register_t

 

The meanings of all the struct members:

 

o       name: The name string of the ICE register.

o       desc: The full description string of the ICE register.

o       addr: The address of the ICE register.

o       bitnr: The bit length of the ICE register.

o       value: The current value of the ICE register.

 

The data struct scan_chain_t is used to describe a scan chain in the JTAG interface of the target CPU. It is defined in the header file include/target.h:

 

typedef struct scan_chain_struct {

   char *name; /* the name of scan chain */

   int bitnr; /* the bit number of testdata */

 

   /* the test data written in and the test data read out */

   u32 writein[MAX_SCANCHAIN_LENGTH];

   u32 readout[MAX_SCANCHAIN_LENGTH];

 

   /* points to the data struct specific to the scan chain */

   void *private;

}scan_chain_t;

 

List 4-3 The definition of the data struct scan_chain_t

 

The meanings of all the struct members:

 

o       name: The name string of this scan chain.

o       bitnr: The bit width of this scan chain.

o       The array writein[]: The data value written into this scan chain recently.

o       The array readout[]: The data value read back from this scan chain recently.

o       private: points the private data of this scan chain, it maybe NULL.

 

The data struct target_t is used to describe the target CPU. Each type CPU core must define a target_t type variable to describe itself. This data struct is defined in the header fileinclude/target.h:

 

typedef struct target_struct {

   int type; /* ARM7TDMI or ARM9TDMI */

   int mode; /* ARM or Thumb */

   int status; /* halt, monitor or running */

   int halt_reason; /* BREAKPT/WATCHPT/DBGRQ */

 

   /* The core registers array and ICE registers array.

    * the last array element must be NULL.

    */

   core_register_t *regs;

   ice_register_t  *ice_regs;

  

   /* target test data registers */

   testdata_reg_t bypass; /* BYPASS regiseter */

   testdata_reg_t idcode; /* Device ID code register */

   testdata_reg_t instruction;  /* instruction register */

   testdata_reg_t scanpath; /* scan path select register */

 

   /* test data of all scan chains */

   u32 sc_num;  /* the number of scan chains of the target */

   u32 active_sc; /* the current active scan chain */

   scan_chain_t sc[MAX_SCANCHAIN_NUM];

   void *private;

}target_t;

 

List 4-4 The definition of the data struct target_t

 

The meanings of all the struct members:

 

o       type: The type of the target CPU core. Its value may be the macro TARGET_TYPE_ARM7TDMI or the macro TARGET_TYPE_ARM9TDMI.

 

o       mode: The mode of the target CPU core. For the CPU based on ARM core, it may be in the normal 32-bit ARM mode, or be in the 16-bit THUMB mode.

 

o       status: The status of the target CPU core. For the CPU based on ARM7TDMI core or ARM9TDMI core, it may be in the normal system running state, debug state or monitor state.

 

o       The pointer regs: It points the core registers array of the target CPU. NOTE: the last array element must be NULL.

 

o       The pointer ice_regs: It points the ICE registers array of the target CPU. NOTE: the last array element must be NULL.

 

o       bypass: represents the bypass test data register of the JTAG interface of the target CPU.

 

o       idcode: Represents the idcode test data register of the JTAG interface of the target CPU.

 

o       instruction: Represents the instruction test data register of the JTAG interface of the target CPU.

 

o       scanpath: Represents the scan path select test data register of the JTAG interface of the target CPU.

 

o       sc_numRepresents the scan chain number of the JTAG interface of the target CPU. For ARM7TDMI core or ARM9TDMI core, its value must be 3.

 

o       active_sc: Represents the current selected scan chain of the JTAG interface of the target CPU.

 

o       The array sc: Represents all the scan chains in the JTAG interface of the target CPU.

 

o       The pointer private: It points the private data of the target CPU. It may be NULL.

 

The data struct target_operation is used to describe a function pointers jump table. It is defined in the header file include/target.h:

 

struct target_operation {

   /* ICE register read/write operations */

   int (*ice_read) (u32 reg_addr, u32 *reg_val);

   int (*ice_write) (u32 reg_addr, u32 reg_val);

 

   /* target cpu core operations */

   int (*halt) (void);

   int (*restart) (void);

 

   /* core registers read/write operations */

   int (*get_core_state) (void); /* read all core regs */

   int (*register_read) (core_register_t *reg);

   int (*register_write) (core_register_t *reg, u32 value);

 

   /* target memory read/write operations */

int (*mem_read8) (u8 *buf, u32 address, u32 length);

int (*mem_write8) (u8 *buf, u32 address, u32 length);

   int (*mem_read16) (u16 *buf, u32 address, u32 length);

   int (*mem_write16) (u16 *buf, u32 address, u32 length);

   int (*mem_read32) (u32 *buf, u32 address, u32 length);

   int (*mem_write32) (u32 *buf, u32 address, u32 length);

};

 

List 4-5 The definition of the data struct target_operation

 

Based on the data structs target_t and target_operation, the target CPU cores interface defines two global pointers target and t_op in the source file src/target/target.c, which point to the current selected target CPU core and its function pointers jump table. These two global pointers are just the unified interface provide to the other JTAGER modules by the target CPU cores interface module. Their definition:

 

/* the unified interface of different target implementation */

struct target_struct *target = NULL;

struct target_operation *t_op = NULL;

 

List 4-6 The unified interface of the target CPU cores module

Chapter 5 The Flash Interface Module

All the source files of the JTAGER flash interface module are placed in the directory src/flash. And the unified flash module interface is defined in the source file src/flash/flash.c.

5.1 The Flash Interface

The data struct flash_t is used to describe a flash chip on your target board. It is defined in the header file include/flash.h:

 

typedef struct flash_chip_struct {

   char *name; /* the flash chip name string */

   u32 start_addr; /* the physical start address */

   int chip_size; /* how many bytes the whole chip has */

   int sector_size; /* how many bytes per sector */

   int block_size; /* how many bytes per block */

   int bit_width; /* it can only be 8, 16 or 32 */

   int cfi_info_size; /* the words number of flash CFI information */

 

   /* flash operations */

   int (* detect) (void);

   int (* cfi_query) (void *buffer);

   int (* erase_sector) (u32 addr);

   int (* erase_block) (u32 addr);

   int (* erase_chip) (void);

   int (* read) (void *buffer, u32 addr, u32 length);

   int (* write) (void *buffer, u32 addr, u32 length);

  

   /*

    * Its alias strings, and the last must be NULL.

    * NOTE: the flexible array member must be at the end of struct.

    */

   char * aliases[];

}flash_t;

 

List 5-1 The definition of the data struct target_operation

 

The meanings of the struct members:

 

o       name: The name string of the flash chip.

 

o       start_addr: The physical start address of the flash chip on your target board.

 

o       chip_size: The size of the whole flash chip. That is, the whole chip contains how many bytes.

 

o       sector_size: The bytes number per flash sector.

 

o       block_size: The bytes number per flash block.

 

o       width: The bit width of the flash chip. It must 8, 16 or 32.

 

o       cfi_info_size: The words (8 bits, 16 bits or 32 bits) number of the CFI information on the flash chip. It may be zero for those chips that have no CFI information on-chip.

 

o       The function pointer detect: Points to the detect function of the flash chip. Generally speaking, we can implement the flash detect function by reading its software ID.

 

o       The function pointer cfi_query: Points to the CFI information read function of this function. It may be NULL for those flash chips that have no CFI information on-chip.

 

o       The function pointer erase_sector: Points to the sector erase function of the flash chip.

 

o       The function pointer erase_block: Points to the block erase function of the flash chip. It may be NULL for those flash chips that haven’t defined flash block.

 

o       The function pointer erase_chip: Points to the chip erase function of the flash chip.

 

o       The function pointer read: Points to the flash read function. The argument addr specifies the physical start address of the flash memory range to read. The argument length specifies how many words to read. Determined by the bit width of the flash chip, the word width may be 8-bit, 16-bit or 32-bit. The read results are saved into the buffer pointed by the pointer buffer.

 

o       The function pointer write: Points to the flash write function. The argument addr specifies the physical start address of the flash memory range to be written. The argument length specifies how many words we will write. Determined by the bit width of the flash chip, the word width may be 8-bit, 16-bit or 32-bit. The buffer pointed by the pointer buffer contains the source data to be written in.

 

Each type of flash chip implemented by JTAGER must define its flash_t type variable to describe itself. For example, The implementation of the flash chip SST39LF/VF160 defines the flash_t type variable in the source file src/flash/sst39vf160.c to describe itself:

 

flash_t sst39vf160 = {

   .name     = NULL,

   .aliases = {"SST39VF160", "SST39LF160", NULL},

   .start_addr = 0x0L, /* start physical address */

   .chip_size   = SST39_SIZE,

   .sector_size    = SST39_SECTOR_SIZE,

   .block_size = SST39_BLOCK_SIZE,

   .bit_width   = 16,

   .cfi_info_size = SST39VF160_CFI_INFO_SIZE,

 

   .detect   = sst39vf160_detect,

   .cfi_query   = sst39vf160_cfi_query,

   .erase_sector   = sst39vf160_erase_sector,

   .erase_block    = sst39vf160_erase_block,

   .erase_chip = sst39vf160_erase_chip,

   .read     = sst39vf160_read,

   .write       = sst39vf160_write,

};

 

List 5-2 The definition of the variable sst39vf160

 

Base on the above flash_t struct, we define a pointers array flashes[] in the source file src/flash/flash.c, to register all the defined flash_t type variables.

 

/* all supported flash chips */

flash_t * flashes[] = {

   &sst39vf160,

   &sst39vf040,

   &sst28sf040,

   &am29f040,

   NULL /* the last element must be NULL */

};

 

List 5-3 The pointer array flashes[]

 

NOTE: the last array element of the above pointer array must be NULL. What’s more, we define the global pointer flash to point to the current selected flash chip:

 

/* current selected flash */

flash_t * flash = NULL;

 

List 5-4 The current selected flash pointer

 

Additionally, we also implement a search function flash_index() in the source file src/flash/flash.c, which can be used to search the pointer array flashes[] by a flash chip name string.

5.2 How to Add the Support to A New Flash Chip

We will discuss how to add the support to a new flash chip in JTAGER in the section. Because of the expansibility of the JTAGER flash interface module, it is very easy to do it. We will discuss it step by step.

 

First, users must the property of the new flash chip that you want to support, for example, the physical start address of the flash chip on your target, its bit width, the size per sector or the size per block. For convenience, we assume the name of the new flash that user wants to add is “xxx”.

 

Then, please add a source file “xxx.c” in the directory src/flash. As you know, all the source codes of the implementation of the new flash chip xxx are placed in this newly added source file.

 

The third step, we define a flash_t type variable xxx in the source file xxx.c:

 

flash_t xxx = {

    .name = “xxx”,

   .aliases     = {"xxx", NULL},

   .start_addr = xxx_START_ADDR, /* start physical address */

   .chip_size   = xxx_CHIP_SIZE,

   .sector_size    = xxx_SECTOR_SIZE,

   .block_size = xxx_BLOCK_SIZE,

   .width       = 16, /* 8, 16 or 32 */

    .cfi_info_size = xxx_CFI_INFO_SIZE,

 

   .detect   = xxx_detect,

   .cfi_query   = xxx_cfi_query,

   .erase_sector   = xxx_erase_sector,

   .erase_block    = xxx_erase_block,

   .erase_chip = xxx_erase_chip,

   .read     = xxx_read,

   .write       = xxx_write,

};

 

List 5-5 The definition of the flash_t type variable xxx

 

The forth step, we encode to implement all involved macros and functions.

 

The fifth step, we register the address of the variable xxx in the pointer array flashes[]:

 

/* all supported flash chips */

flash_t * flashes[] = {

   &sst39vf160,

    &sst39vf040,

   &sst28sf040,

   &am29f040,

    &xxx,

   NULL /* the last element must be NULL */

};

 

List 5-6 the modified pointer array flashes[]

 

The sixth step, add the source file name “xxx.c” into the file Makefile.am in the directory src/flash:

 

libflash_a_SOURCES = \

   flash.c \

    sst39vf160.c \

   sst39vf040.c \

   sst28sf040.c \

   am29f040.c \

    xxx.c

 

List 5-7 Add the source file name “xxx.c” into the source files list

 

The seventh step, re-generate the script file configure and all Makefile.in files according to the following steps:

 

#aclocal

#autoheader

#automake

#autoconf

 

The last step, re-compile and re-install JTAGER. Please refer to the documentation JTAGER User Manual.

Index

None.

SourceForge.net Logo

没有评论: