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

Ÿ       4 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 macro cmd_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 <stdio.h>

#include <stdlib.h>

#include <getopt.h>

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/arm7tdmi implement 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 file include/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 file include/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 file include/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