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是一个由命令行驱动的程序,它主要由4个模块组成:1)命令解析器(command parser)模块;2JTAG仿真器接口模块;3)目标机CPU core接口模块;4Flash接口模块。图1可以说明这4个模块之间的关系:

 

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

|              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

所有与JTAGER命令解析器实现相关的源代码文件都被保存到目录src/cmd下。JTAGER命令解析器可以分为两大实现部分:1)统一的命令解析器接口;2)具体的JTAGER命令实现。统一的命令解析器接口实现在源文件src/cmd/command.c中,它也包含两部分:一个包含所有JTAGER命令结构指针数组和一个命令调度器(Command Dispatcher),命令调度器是JTAGER命令解析器的唯一入口。

 

2.1 The Data Struct of JTAGER Commands

数据结构jtager_cmd_t被用来描述一个JTAGER命令,它定义在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 数据结构jtager_cmd_t的定义

 

各数据结构成员的含义如下:

 

l       字符串指针name:指向该命令的名字字符串。每一个JTAGER命令都必须有一个唯一的名字字符串。

l       指针description:该命令的简单描述字符串。

l       选项指针long_options:指向一个包含该JTAGER命令的命令选项数组。注意:最后一个数组元素必须是{0, 0, 0, 0}

l       non_option_args:该JTAGER命令的非选项参数个数。

l       函数指针help:指向该JTAGER命令的帮助函数。

l       函数指针run:指向该JTAGER命令的运行函数。命令调度器最终将过这个函数指针来调用特定命令的运行函数。

 

每一个JTAGER命令都对应有一个jtager_cmd_t类型的变量来描述这个命令,其地址也被登记在jtager_cmd_t类型的指针数组jtager_cmds[]中。这样,我们就可以通过指针数组jtager_cmds[]跳转到任何一个我们想要的JTAGER命令。实现在src/cmd/command.c文件中的函数command_index()以及基于这个函数的宏cmd_index()可以帮助我们通过命令名来检索jtager_cmds[]指针数组。

2.2 The Commands Dispatcher

命令调度器――函数parse_cmdline()实现在源文件src/cmd/command.c中。它是JTAGER命令解析器的唯一入口。同时这个函数也是JTAGER命令解析全过程的总控函数,它最终会根据命令名向下调用指定JTAGER命令的运行函数。正因此,parse_cmdline()函数才被称为“命令调度器”。

 

命令调度器包含以下4个执行步骤:

 

l       Step 1:分解命令行字符串

 

这一步将以空格字符为分隔符,把整个命令行字符串分解成多个子字符串数组,并将它们的指针保存在字符串指针数组cmdline_argv[]中,同时子字符串数组的个数也将被保存在变量cmdline_argc中。注意:cmdline_argv[0]总是指向命令行中的JTAGER命令名,因此cmdline_argc的最小值是1,而cmdline_argv[cmdline_argc]总是被设置为NULL

 

这一步的目的是模拟C语言main()函数的参数argc和参数argv,从而使得各JTAGER命令的运行函数可以调用glibc函数getopt_long()来解析JTAGER命令选项和参数。

 

l       Step 2:检索相应的JTAGER命令结构变量

 

经过上一步的分解后,字符串指针cmdline_argv[0]将总是指向命令行中的JTAGER命令名。因此可以以它为参数调用cmd_index宏来在jtager_cmds[]指针数组中进行检索。

 

l       Step 3:检查JTAGER命令选项的合法性

 

这一步骤通过调用函数match_option_args()来检查当前命令行中的选项和参数是否就是指定JTAGER命令所期望的选项和参数。

 

这一步骤的需要是因为glibcgetopt_long()函数存在一个丑陋的BUG。例如,假设有这样一个hello程序,它可以接收一个长名字选项--list,其源码如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 hello.c源文件

 

对于这样一个hello程序,如果我们以命令行“hello --li 0x11”,它将仍然打印消息“The --list option is specified.”,而且它不能识别非法的非选项参数0x11!

 

l       第四步:调用指定JTAGER命令的运行函数

 

通过调用指定JTAGER命令的run函数,命令调度器将控制权交给特定JTAGER命令的run函数,由它来对当前命令行做进一步的解析。

 

2.3 The Run Function of JTAGER Commands

JTAGER命令的run函数是JTAGER命令解析器的最后一个过程。JTAGER命令的run函数的最主要任务是:1)调用getopt_long()函数来解析它自己的命令选项、选项参数和非选项参数。2)根据不同的选项执行不同的操作,

这通常需要向下调用Flash接口、Target CPU Core接口和JTAG接口。

Chapter 3 The JTAG Interface Module

To be continued

Chapter 4 The Target CPU Cores Module

与目标机的CPU Core接口实现相关的源文件都放在src/target目录中,其中,子目录src/target/arm7tdmi下的源文件实现ARM7TDMI core,子目录src/target/arm9tdmi下的源文件实现ARM7TDMI core

 

4.1 The Data Structs

数据结构core_register_t被用来描述CPUcore寄存器,比如:ARM core中的R0CPSR等寄存器。该数据结构定义在include/target.h头文件中:

 

typedef struct core_register_struct {

   char *name;

   u32 value;

}core_register_t;

 

List 4-1 数据结构core_register_t的定义

 

其中,name成员表示这个寄存器的名字,value成员表示这个寄存器的当前值。

 

数据结构ice_register_t被用来描述ARM7TDMIARM9TDMI core内的EmbeddedICERT logic中的ICE寄存器,它定义在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 数据结构ice_register_t的定义

 

各数据成员的含义如下:

 

l       nameICE寄存器的名字字符串。

l       desc:完整的ICE寄存器名字字符串。

l       addrICE寄存器的地址。

l       bitnrICE寄存器的位长度。

l       valueICE寄存器的当前值。

 

数据结构scan_chain_t被用来描述目标机CPUJTAG接口中的一个扫描链,它定义在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 数据结构scan_chain_t的定义

 

各成员的含义如下:

 

l       name:这个扫描链的名字。

l       bitnr:这个扫描链的位宽度。

l       数组writein[]:上一次写入扫描链的数据值。

l       数组readout[]:上一次从扫描链读出的数组值。

l       指针private:指向这个扫描链的私有数据,它可以是NULL

 

我们用数据结构target_t来描述目标机CPU,每种类型的CPU core实现都必须定义一个target_t类型的变量来表示它自己。该数据结构定义在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 数据结构target_t的定义

 

各数据结构成员的含义如下:

 

l       type:目标机CPU Core的类型。其值可能是TARGET_TYPE_ARM7TDMITARGET_TYPE_ARM9TDMI

l       mode:目标机CPU的模式。对于ARM core而言,其CPU可能处于正常的32ARM模式下,也可能处于16位的THUMB模式下。

l       status:目标机CPU的状态。对于ARM7TDMI coreARM9TDMI core而言,CPU可能处于正常的系统运行状态、调试状态或monitor状态。

l       regs指针:指向目标机CPUcore寄存器数组。NOTE:最后一个数组元素必须是NULL

l       ice_regs指针:指向目标机CPUICE寄存器数组。NOTE:最后一个数组元素必须是NULL

l       bypass寄存器:表示目标机CPUJTAG接口中的bypass测试寄存器。

l       idcode寄存器:表示目标机CPUJTAG接口中的idcode测试寄存器。

l       instruction寄存器:表示目标机CPUJTAG接口中的instruction测试寄存器。

l       scanpath寄存器:表示目标机CPUJTAG接口中的扫描路径选择寄存器。

l       sc_num:表示目标机CPUJTAG接口中的扫描链个数。对于ARM7TDMI CoreARM9TDMI Core来说,这个值应该是3

l       active_sc:表示目标机CPUJTAG接口中当前选择的扫描路径。

l       数组sc:目标机CPUJTAG接口中的扫描链。

l       指针private:指向目标机CPU特定的数据,它可能是NULL

 

数据结构target_operation描述了一个函数跳转指针表,也即它定义了每种CPU core实现都应该提供的操作函数。该结构定义在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 数据结构target_operation的定义

 

基于数据结构target_ttarget_operation,目标机CPU Core接口在src/target/target.c源文件中定义了两个全局指针targett_op,分别指向当前选定的目标机CPU core及其操作函数指针跳转表。这两个全局指针也就是目标机CPU core接口模块向外部提供的统一接口。它们的定义如下:

 

/* the unified interface of different target implementation */

struct target_struct *target = NULL;

struct target_operation *t_op = NULL;

 

List 4-6 目标机CPU core模块的统一接口

Chapter 5 The Flash Interface Module

JTAGERFlash接口模块实现相关的源文件都被放在目录src/flash下。其中源文件src/flash/flash.c实现了这个模块的接口。

 

5.1 The Flash Interface

数据结构flash_t被用来描述目标板上的flash芯片。它定义在include/flash头文件中:

 

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 数据结构target_operation的定义

 

各成员的含义如下:

 

l       字符串指针数组name[]:该flash芯片的名字。NOTE:最后一个数组元数必须是NULL

l       start_addr:该flash芯片在目标板上的起始物理地址。

l       chip_size:该flash芯片的大小,单位是字节。

l       sector_size:该flash芯片中每个扇区包含多少字节。

l       block_size:该flash芯片中每个块包含多少一个字节。

l       width:该flash芯片的位宽,比如:16位或32位。

l       函数指针detect:指向该flash芯片的检测函数。通常,我们可以通过读取flash芯片的软件ID来检测该flash芯片是否存在。

l       函数指针cfi_query:指向flash芯片的CFI信息读取函数。

l       函数指针erase_sector:指向一个用来擦除指定flash扇区的函数。

l       函数指针erase_block:指向一个用来擦除指定flash块的函数。

l       函数指针erase_chip:指向一个用来擦除整个flash芯片的函数。

l       函数指针read:指向读指定flash内存范围的函数。参数addr指定待读取的flash内存范围的起始物理地址;参数length指定读取多少个字(word),至于字的宽度则由flash芯片的位宽来决定。假如flash芯片是16位的,则一个字的宽度就是16位,即两个字节。假如flash芯片是32位的,则一个字的宽度就是32位,即4个字节。读回的结果将被保存到buffer所指向的缓冲区。

l       函数指针write:指向写指定flash内存范围的函数。参数addr指定待写入的flash内存范围的起始物理地址;参数length指定将要写入多少个字(word),至于字的宽度也是由flash芯片的位宽来决定的。缓冲区buffer中存有待写入的源数据。

 

每个被实现的flash芯片都必须定义一个flash_t类型的变量来描述它自己。比如,SST39LF/VF160芯片的实现就定义了flash_t类型的变量sst39vf160来描述它自己:

 

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 sst39vf160变量的定义

 

基于上述数据结构,我们在源文件src/flash/flash.c中定义了指针数组flashes[]来登记所有被定义的flash_t结构类型变量,如下所示:

 

/* all supported flash chips */

flash_t * flashes[] = {

   &sst39vf160,

   &sst39vf040,

   &sst28sf040,

   &am29f040,

   NULL /* the last element must be NULL */

};

 

List 5-3 指针数组flashes

 

注意:上述指针数组的最后一个元素必须是NULL。此外,我们还定义了全局指针flash来指向当前选定的flash芯片。如下所示:

 

/* current selected flash */

flash_t * flash = NULL;

 

List 5-4 当前指针flash

 

此外,我们还在src/flash/flash.c源文件中实现了一个检索函数flash_index()来根据芯片名字来在指针数组flashes[]中检索相应的flash_t结构变量。正是这三者构成了JTAGERflash模块的统一接口。

 

5.2 How to Add the Support to A New Flash Chip

本节将讨论如何让JTAGER支持新的flash芯片。在JTAGER中,由于Flash模块具有良好的可扩展性,因此实现这一点是非常容易的。下面我们将一步一步地讨论这一点。

 

首先,用户应该了解您想要支持的新flash芯片的属性,比如:它在目标板上的起始物理地址、位宽、每个扇区的大小或每个块的大小等。为了叙述的方便,我们假定用户想要增加的新flash芯片的名字为“xxx”。

 

接下来,在src/flash目录下增加一个源文件xxx.c。所有与xxx芯片实现相关的源代码都将被放置在这个新增加的xxx.c源文件中。

 

第三步,在源文件xxx.c中定义一个flash_t结构类型的变量xxx,如下:

 

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 flash芯片xxxflash_t结构变量

 

4步:编码实现List 5-5中列出各个函数和宏。

 

5步:将结构变量xxx的地址登记到指针数组flashes[]中。如下:

 

/* all supported flash chips */

flash_t * flashes[] = {

   &sst39vf160,

    &sst39vf040,

   &sst28sf040,

   &am29f040,

    &xxx,

   NULL /* the last element must be NULL */

};

 

List 5-6 指针数组flashes

 

6步:在src/flash目录的Makefile.am文件中增加源文件xxx.c,如下:

 

libflash_a_SOURCES = \

   flash.c \

    sst39vf160.c \

   sst39vf040.c \

   sst28sf040.c \

   am29f040.c \

    xxx.c

 

List 5-7 xxx.c增加到源文件列表中

 

7步:按照下列步骤重新生成configure脚本和各个Makefile.in文件:

 

#aclocal

#autoheader

#automake

#autoconf

 

最后一步:重新进行编译和安装JTAGER。请参考文档《JTAGER User Manual》。

Index

None.