Version: 0.1.0
November 2003
Rongkai Zhan (Chinese Name: 詹荣开)
2 The JTAGER Commands Parser
o
2.1 The Data
Struct of JTAGER Commands
o
2.3 The Run
Function of JTAGER Commands
4 The
Target CPU Cores Module
5 The Flash
Interface Module
o
5.2 How to Add
the Support to A New Flash Chip
Index
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 |
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.
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
description:The 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.
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.
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.
(To be added)
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.
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_num:Represents 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 |
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.
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.
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》.
None.