inAccess : IAN Projects ianjtag

inAccess Networks

ianjtag tools

inAccess Networks's JTAG tools

Nick Patavalis (npat@inaccessnetworks.com)
Dimitris Economou (decon@inaccessnetworks.com)

Athens, 4 Dec 2001

Contents

News

4 Dec 2001. [version 1.2 released]

A new version (v1.2) of "ianjtag tools" was released, which adds support for the Corelis PCI1149 board. PCI1149 can be used as the hardware-interface between the host system and the JTAG port of the target system. In order to support this board, the libraries and drivers that ship with it are required (they are obviously not included in "ianjtag tools"). This code has been reported to compile and run, under both: linux, and Win32 using cygwin.

Responsible for the Corelis PCI1149 support is Peter Smith, of InfiniCon Systems.

20 Nov 2001. [bugfix / update version 1.1 released]

A new version (v1.1) of "ianjtag tools" was released with minor additions and bug-fixes. Some bells and whistles were added to the "ianjflash" tool (new commands, better error reporting, etc) and several annoying bugs were fixed. Additionally the JTAG command codes (i.e. the binary values for commands like EXTEST, SAMPLE, IDCODE, etc) were moved from the low-level jtag module to the processor support modules; now every processor can have its own jtag command codes, as IEEE 1149.1 requires. Finaly the timing of the elementary jtag cycle was sligtly modified, to make it more compatible to the standard (or so we hope!). As allways comments, requests, and feedback is wellcome.

We would especially wellcome some feedback on the timing modifications (see file: "jtag.c", function "putp()") mentioned above, as we are worried that these could be dependent on the hardware interface used to connect to the processor's JTAG port. Therefore we are especially interested to receive some comments from people that have succeeded to compile and run (test) the tools, in respect to the hardware interfaces they are using.

Overview

"ianjtag tools" is a collection of code and a set of tools for using the JTAG interface (present in most modern microprocessors) to perform hardware tests, and for programming Flash Memory Devices connected to the processor's bus. It is especially useful in embedded systems development projects for performing initial system tests and for bootstrapping the prototype systems. "ianjtag tools" run on a "host system" (e.g. a desktop computer with Linux) and access the "target system" (e.g. the embedded system's CPU board) through a simple 5-lines hardware interface. In the present implementation the host system's parallel port is used as the hardware interface, though other arrangements can very easily be supported. Operations like these are believed to be very system-dependent, and, to our knowledge, there is no other free tool available to address these needs. "ianjtag tools" takes a modular approach to the problem and, though it currently supports the Intel StrongARM SA-1110 as a target processor, and the Intel StrataFlash ICs as flash memory devices, it can easily be extented to support any other target hardware configuration by writing simple modules that can be incorporated to the infrastructure.

Great care has been taken to separate and isolate the system dependencies and encapsulate them in modules with generic interfaces. "ianjtag tools" contains modules dependent to the host-system and the hardware I/O interface used to access the target processor's JTAG port, modules dependent to the target-processor's architecture, and modules dependent to the flash memory device in use. By separating these dependencies it is possible to support a variety of host and target systems by augmenting the code with additional modules, without requiring to rewrite everything. Furthermore it makes it possible to have a single set of tools that can, to some extent, automatically detect, and adapt itself to operate with, any of the supported target systems.

Building and using this code

A very simple "Makefile" is provided for this purpose, which is thoroughly commented. You should have no problem making it work for almost any Linux system. For other operating systems some modifications will be required, not so much to the 'Makefile' (which is fairly general) but to the system dependent portions of the code. Anyway, "ianjtag tools" was written with portability in mind so the task of porting it to a new (even very different) system should not be really hard. Furthermore extensive and very detailed documentation of the code's structure and operation is provided in this file. Additional documentation can be found inside the source-code itself in the form of comments. Some additional background information in the form of an introduction to JTAG and theory of operation is also available.

Source code files and modules

"ianjtag tools" consists of several source-code files, all written in the C programming language, and organized in modules as described in this section.

jtag.c, jtag.h:
Low level jtag module

Contains code for doing low-level hardware I/O (parallel port I/O under Linux in the present implementation), and code for handling the JTAG state-machine. The low-level I/O code is hardware and O/S dependent, but, due to its small size and minimum complexity, it can trivially be rewritten to support any arrangement. The code that handles the JTAG state-machine has no hardware or O/S dependencies. The low-level jtag module exports a very simple interface that gives its users the ability to perform JTAG cycles easily.

pjtag.h:
Processor Support Module (PSM) interface definition

The processor support modules (one for each supported target processor) hide the processor-specific JTAG complexities behind a common processor-invariant interface, supporting operations like pin-state (level) reading and setting, and various types of bus-cycles. While in its present state the PSM interface supports only a few operations ---mainly targeted at flash-memory handling--- additional operations can be defined, and implemented, if the need arises. Obviously not all PSM operations make sense for all processor architectures, so each actual PSM is required to implement only the relevant subset (e.g. the PSM for a 16bit processor may not implement the 32bit-wide bus cycle operations, and the PSM for a 32bit processor may not implement certain 16bit bus cycles).

pjtag_sa1110.h, pjtag_sa1110.c:
Processor support module for Intel StrongARM SA-1110.

fjtag.h:
Flash Support Module (FSM) interface definition.

The flash support modules (one for each flash device type and configuration) hide the device-specific flash programming complexities behind a common device-invariant interface, by exporting general purpose functions like 'read', 'write', 'verify', etc... Generally each Flash memory device and configuration has its own FSM, but very similar devices (or small variations in the configuration) may be supported by the same module. The flash support modules use the PSM interface to perform low-level processor-specific operations, so the same FSM can be used to support a device and configuration irrespective to the system's processor architecture.

fjtag_isf2x16.h, fjtag_isf2x16.c:
Flash support module for the Intel StrataFlash in 2x16bit configuration.

ianjflash.c:
Command line tool (application and user interface code) for programming flash memory devices. Uses the flash support module interface to perform the actual device operations, so it can support any system there is a PSM and an FSM for. Usage information on "ianjflash" are given when the tool is run with the "--help" argument.

Makefile:
Simple GNU-Make 'Makefile' for building the 'ianjtag tools'. Customize it according to your system requirements.

Module interfaces

Follows a description, in some detail, of the interfaces exported by each of the modules presented above. Read this if you plan to add support for a different processor architecture, a different type of flash-memory-device or configuration, if you plan to write a new tool that uses "ianjtag tools"'s infrastructure, or if you plan to port "ianjtag tools" to use a different hardware interface or to run on a different host system.

Low-level JTAG module (files: jtag.h, jtag.c)

This module can be thought to consist of these two parts:

The low-level hardware I/O interface code (files: jtag.h, jtag.c)

It is dependent to the host-system and the JTAG hardware interface in use. In the present implementation it works under Linux and uses the host system's parallel-port as the JTAG hardware interface. The JTAG interface requires five signal lines (four outputs, and one input, as seen from the host system). These signals are connected to the parallel port pins like this:
   TCK  <---- Pin 2  (Data 0)
   TMS  <---- Pin 3  (Data 1)
   TDI  <---- Pin 4  (Data 2)
   TRST <---- Pin 5  (Data 3)
   TDO  ----> Pin 11 (Busy)
At the other end of the interface, the these signals are connected to the target system processor's JTAG port, and some glue circuitry may be required in the middle for voltage level adaptation, filtering, etc. For example, in our systems the hardware interface looks like this:
   +------------+      +--------+       +-------+        +------+
   |         TCK<------+ Buff-  +-------<2      |        |      |
   | TARGET  TMS<------+ ering  +-------<3      |   25   |      |
   | JTAG    TDI<------+ &      +-------<4 DB25 |===//===| HOST |
   | Port   TRST<------+ Level  +-------<5      |        |      |
   |         TDO>------+ Adapt. +------->11     |        |      |
   +------------+      +--------+       +-------+        +------+
The low-level hardware I/O interface code provides these simple functions, which you will need to re-code if you want to support a different type of host-system or hardware interface:

Prototype:
int jtag_init (void *opts);

File:
jtag.c, jtag.h

Description:
Initialize the host system's JTAG hardware I/O interface.

Arguments:
opts:
Interface options which are dependent on the interface type. NULL MUST work on the most common cases. In the present implementation 'opts' is a pointer to an array of (unsigned long) I/O address, terminated by a 0 (NULL) address. These addresses will be tried-out in the given order as parallel ports until one succeeds.

Returns:
Non-negative on success. Negative on error.

Notes:
This function is exported by the low-level JTAG module, and called by the user to initialize the interface. Actually it is the only function exported by the low-level hardware I/O code.

Prototype:
static inline void rstp (void);

File:
jtag.c

Description:
Apply a reset pulse on the TRST, JTAG reset line.

Notes:
A reset pulse is generated by driving and holding the line low for a few micro-seconds and the driving and keeping it high. Though this function is not typically exported (and therefore not called by the "user"), it is called by the low-level JTAG state machine handling code.

Prototype:
static inline int putp(int tdi, int tms);

File:
jtag.c

Description:
Drive the TDI and TMS JTAG input lines to the given levels, while pulsing the TCK line. Read and return the level of the TDO output line.

Arguments:
tdi:
0 or 1. Level to drive the TDI line at.

tdo:
0 or 1. Level to drive the TMS line at.

Returns:
0 or 1. Level of the TDO JTAG output line.

Notes:
The sequence goes like this:
* drive TCK high, and TDI and TMS to the given levels.
* drive TCK low, and keep TDI and TMS to the given levels.
* read, and return, the level of the TDO pin.

These are the only functions you will need to rewrite in order to support an different JTAG hardware interface, or a different host system. The functions "io_access_on()" and "io_access_off()" are privately called by "jtag_init()", and are therefore more like "parts" of "jtag_init()".

The low-level JTAG state-machine handling code (files: jtag.h, jtag.c)

Other than calling the functions of the low-level hardware I/O code, discussed above, the JTAG state-machine handling code has no other host or target system dependencies, so there's probably no reason to touch its implementation. You will, however, need to use (call) the interface exported by this code in order to write a Processor Support Module. JTAG state machine handling code's interface consist of the following functions:

Prototype:
unsigned long jtag_fillword(unsigned char *input, int from, int len, int skip)

File:
jtag.c, jtag.h

Description:
Stuffs values from an unsigned-char array in an unsigned-long (word), interpreting each array element as a bit-value.

Arguments:
input:
Unsigned-char array. Each element must be either 1 or 0.

from:
Start stuffing from this array element

len:
Stuff this many elements

skip:
Stuff every "skip" element. A negative skip will force the function to stuff backwards

Returns:
the stuffed word as an unsigned long

Notes:
You can better understand this function by example:
if input[] contains {0,1,1,0,1,0,1,0,1,1,1,0,0,0,0,1,0,1}
- then jtag_fillword(input, 8, 8, 1) will return 0x00000087
- then jtag_fillword(input, 8, 5, -1) will return 0x00000015

Prototype:
void jtag_reset (void);

Description:
Reset the target system's JTAG machine, in the most low-level (powerful) way, e.g. by pulsing the TRST JTAG pin.

Prototype:
void jtag_test_logic_reset (void);

Description:
Bring the target system's JTAG state machine to the "test logic reset" state.

Prototype:
void jtag_run_test_idle (void);

Description:
Bring the target system's JTAG state machine to the "run test idle" state. On its way there the state machine will pass from the "test logic reset" state, and the interface will be reset.

Prototype:
void jtag_do_ir_path (register unsigned long iri, register unsigned long *iro, register int irsz);

Description:
Perform an "IR path" (state cycle), starting from the "run test idle" state and ending at the "run test idle" state. The following state-path is traversed:
 
      *-> run test idle 
      --> select dr scan 
      --> select ir scan 
      --> capture ir 
      --> shift ir --> ...'irsz' times... --> shift ir 
      --> exit1 ir 
      --> update ir 
      --> run test idle ->*
    
This operation has the effect of writing a value to the JTAG Instruction Register, and thus issuing a "JTAG command".

Arguments:
iri:
Instruction Register in. One bit from this (starting with the LSB) will be shifted-in the JTAG Instruction Register every time the "shift ir" state is entered.

iro:
Instruction Register out. Will be filled (LSB first) with the bits shifted-out from the JTAG Instruction Register, every time the "shift ir" state is left.

irsz:
Instruction register size; the number of bits to shift in and out of the JTAG IR.

Notes:
Think of this operation as writing a value to the JTAG Instruction Register, while reading-back its previous contents. In this sense "iri" is the value to be written, "iro" is the value previously held by the Instruction Register, and "irsz" is the width of the Instruction Register in bits.

Prototype:
void jtag_do_dr_path (register unsigned char *dri, register unsigned char *dro, register int drsz);

Description:
Perform a "DR path" (state cycle), starting from the "run test idle" state and ending at the "run test idle" state. The following state-path is traversed:
 
      *-> run test idle 
      --> select dr scan 
      --> capture dr 
      --> shift dr --> ...'drsz' times... --> shift dr 
      --> exit1 dr 
      --> update dr 
      --> run test idle ->*
    
This operation has the effect of writing a value to the JTAG Data Register, and, depending on the JTAG command previously issued, setting the processor's output pins states, or reading the processor's ID code.

Arguments:
dri:
Data Register in. Every element must have a value of either 1 or 0. One element of this array (interpreted as a bit-value, and starting with dri[0]) will be shifted-in the JTAG Data Register every time the "shift dr" state is entered.

dro:
Data Register out. Each element (starting with dro[0]) will be filled with the bit shifted-out from the JTAG Data Register, every time the "shift dr" state is left.

drsz:
Data register size in bits. The number of bits to shift in and out of the JTAG DR.

Notes:
Think of this operation as writing a value to the JTAG Data Register, while reading-back its previous contents. In this sense "dri" contains the value to be written, "dro" is filled with the value previously held by the Data Register, and "drsz" is the width of the Data Register in bits.

Prototype:
void jtag_do_ir_dr_path (register unsigned long iri, register unsigned long *iro, register int irsz, register unsigned char *dri, register unsigned char *dro, register int drsz);

Description:
Perform an "IR path" (state cycle), followed by a "DR path" (state cycle) starting from the "run test idle" state and ending at the "run test idle" state. The following state-path is traversed:
      *-> run test idle 
      --> select dr scan 
      --> select ir scan 
      --> capture ir 
      --> shift ir --> ...'irsz' times... --> shift ir 
      --> exit1 ir 
      --> update ir 
      --> select dr scan 
      --> capture dr 
      --> shift dr --> ...'drsz' times... --> shift dr 
      --> exit1 dr 
      --> update dr 
      --> run test idle ->*
    

Arguments:
iri:
Instruction Register in. One bit from this (starting with the LSB) will be shifted-in the JTAG Instruction Register every time the "shift ir" state is entered.

iro:
Instruction Register out. Will be filled (LSB first) with the bits shifted-out from the JTAG Instruction Register every time the "shift ir" state is left.

irsz:
Instruction register size. The number of bits to shift in and out of the JTAG IR.

dri:
Data Register in. Every element must have a value of either 1 or 0. One element of this array (interpreted as a bit-value, and starting with dri[0]) will be shifted-in the JTAG Data Register every time the "shift dr" state is entered.

dro:
Data Register out. Each element (starting with dro[0]) will be filled with the bit shifted-out from the JTAG Data Register every time the "shift dr" state is left.

drsz
Data register size in bits. The number of bits to shift in and out of the JTAG DR.

Notes:
Think of this operation as a "jtag_do_ir_path()" immediately followed by a "jtag_do_dr_path()"

Processor Support Modules (files: pjtag.h, pjtag_xxx.c, pjtag_xxx.h)

Each processor support module exports a single function named "pjtag_xxx_init()", where "xxx" refers to the specific processor supported by the module (e.g. "pjtag_sa1110_init()"). This function initializes the module and fills-in a "PSM interface structure", a pointer to which is supplied by the caller. Subsequent accesses to the module's services are performed only through this interface structure, which has been initialized and contains PSM variables, pointers to PSM variables, and function pointers. This initialization function is defined as follows:

Prototype:
int pjtag_xxx_init (struct pjtag_if_s *s);

Description:
Detects the processor by reading its JTAG ID code, and initializes the processor support module. If the processor is not supported the function returns something negative.

Arguments:
s:
This structured is filled-in by this function, and subsequently serves as the single access point to all the services of the PSM.

Returns:
Non-negative, if the processor was detected and matched and the module was initialized successfully. Negative otherwise.

Notes:
PSM users are supposed to call the "pjtag_xxx_init()" functions for every available PSM, one after the other, until a call returns a non-negative value, which means that the processor has been detected and can be supported. From there-on the "s" structure can be used to access the services of the PSM regardless of which PSM this is.

An example may clarify things. Assume you have three PSM modules each exporting its own initialization function, say: "pjtag_sa1110_init", "pjtag_mc860_init()", "pjtag_mk68k_init()". The way to go is to call these functions in order until one call succeeds like this:

    
    struct pjtag_if_s s;

    do {
        if ( pjtag_mc860_init(&s) >= 0)
           break;
        if ( pjtag_sa1110_init(&s) >= 0)
           break;
        if ( pjtag_mk68k_init(&s) >= 0)
           break;
        printf("Fuck! Processor not supported!\n");
        exit(1);
    } while(0);
Of course you can devise more clever ways to code this, like having an array of pointers to initialization functions, and so on; but for our little example this will do. Now, even if you don't know which specific call succeeded (which you don't), you can access the module's services through the "s" structure like this:
     s->set_pins(dri, dro);
And that's the whole point of having the PSMs export only a single init function each and providing the rest of their interfaces through a structure filled-in by init: It is something like having a simple application-level dynamic linking feature, or (if you 're into the OOP stuff) like having some sort of poor man's dynamic polymorphism.

The interface structure, which must be filled-in by every PSM init function is defined in the file "pjtag.h", and goes like this:

  struct pjtag_if_s {
      const char *name; /* processor name */

      int irsz;         /* JTAG instruction-register size */
      int idrsz;        /* JTAG id-register size */
      int bsrsz;        /* JTAG bsr-register size */
      const char * const * bsr_names; 
                        /* names of BSR cells */
      const char *bsr_defaults; 
                        /* safe defaults for BSR cells */

      struct pjtag_cmds_s jtag_cmds; 
                        /* codes for standard JTAG commands */

      int databus_width; 
                        /* width of processor's data bus */
      int addrbus_width;   
                        /* width of processor's address bus */
      int *bsr_databus_in; 
                        /* input-cell number for each databus bit */
      int *bsr_databus_out;     
                        /* output-cell number for each databus bit */
      int *bsr_addrbus;         
                        /* cell number for each databus line */

      /* set and read all BSR cells */
      void (*set)(unsigned char *, unsigned char *);

      /* bus-cycles for flash devices */
      void (*flw32)(unsigned long, unsigned long); 
                        /* wr. a 32bit word */
      void (*flw32n)(unsigned long *, unsigned long *, unsigned long);
                        /* wr. n 32bit words */
      void (*flr32)(unsigned long, unsigned long *); 
                        /* rd. a 32bit word */
      void (*flr32n)(unsigned long *, unsigned long *, unsigned long);
                        /* rd. n 32bit words */
  };
The stucture jtag_cmds_s contains codes (binary values) for the standard JTAG commands like EXTEST, INTEST, etc. It is defined as follows:
  struct pjtag_cmds_s {
      long extest;
      long sample;
      long clamp;
      long highz;
      long idcode;
      long bypass;
  };
Follows a description of the pjtag_if_s structure's members. Succumbing to the OOP terminology, I will call the non-callable members "attributes" and the callable members "methods". Not all PSMs are required to support all the defined methods. If a PSM wishes not to support a method (e.g. because it makes no sense for the respective processor) it just initializes the respective interface-structure member with 0 (NULL).

Attribute:
const char *name;

Description:
String containing the name of the detected processor

Attribute:
int irsz;

Description:
Width (in bits) of the processor's JTAG IR register

Attribute:
int idrsz; Description:
Width (in bits) of the processor's JTAG ID register

Attribute:
int bsrsz;

Description:
Width (in bits) of the processor's JTAG BSR register

Attribute:
const char * const * bsr_names;

Description:
Array of name-strings, one for each BSR cell.

Notes:
bsr_names[10], contains (a pointer to) the name-string of the tenth cell of the processor's BSR.

Attribute:
const char * bsr_defaults;

Description:
Array of safe default values, one for each BSR cell.

Notes:
bsr_defaults[10], contains a safe default value (either 1 or 0) for the tenth cell of the processor's JTAG BSR.

Attribute:
struct pjtag_cmds_s jtag_cmds;

Description:
Structure containing the codes (binary values) for the standard JTAG commands (like EXTEST, HIGHZ, etc).

Notes:
Fields for the following commands are currently defined in the structure: EXTEST, SAMPLE, CLAMP, HIGHZ, IDCODE, BYPASS. A negative code indicates that the respective command is not defined for the specific processor.

Attribute:
int addrbus_width;

Description:
Width (in bits) of the processor's address bus.

Attribute:
int databus_width;

Description:
Width (in bits) of the processor's data bus.

Attribute:
int *bsr_databus_in;

Description:
Array containing the BSR input-cell numbers corresponding to each of the processor's databus lines.

Notes:
bsr_databus_in[10] contains the BSR input-cell number for the tenth data-bus line (starting form 0), i.e. for D10. Since the databus is always bidirectional, there are two BSR cells corresponding to each databus line; one input cell and one output cell.

Attribute:
int *bsr_databus_out;

Description:
Array containing the BSR output-cell numbers corresponding to each of the processor's databus lines.

Notes:
bsr_databus_out[10] contains the BSR output-cell number for the tenth data-bus line (starting form 0), i.e. for D10. Since the databus is always bidirectional, there are two BSR cells corresponding to each databus line; one input cell and one output cell.

Attribute:
int *bsr_addrbus;

Description:
Array containing the BSR cell numbers corresponding to each of the processor's address-bus lines.

Notes:
bsr_addrbus[10] contains the BSR cell number for the tenth address-bus line (starting form 0), i.e. for A10.

Method:
void set_pins (unsigned char *dri, unsigned char *dro);

Description:
Sets all the processor's pins (i.e. JTAG BSR cells) to the given states and "returns" the previous pin states (i.e. captured BSR contents).

Arguments:
dri:
Input argument. Each element of this array contains the state of the respective BSR cell (either 1 or 0), and therefore the pin-state to be set.

dro:
Output argument. Each element of this array will be filled with the state (either 1 or 0) the respective BSR cell had, before setting the new pin states (i.e. with the state of the respective cell of the captured BSR)

Notes:
"dri" must contain "bsrsz" (see attributes above) elements, and the "dro" array must be long enough to keep "bsrsz" elements.

Method:
void flw32 (unsigned long address, unsigned long data);

Description:
Perform a 32bit-wide write-cycle on the give address, with the the given data. The cycle will be good for non-brush-write flash devices (and possibly for other devices too).

Arguments:
address:
Address to perform the write on.

data:
Data to be written.

Notes:
The least significant byte of data will be written in the byte at "address", while the most significant byte will be written in the byte at "address+3" (i.e. little-endian arrangement), regardless of the underline hardware (if the hardware is big-endian, this function must perform the conversion itself).

Method:
void flw32n (unsigned long *address, unsigned long *data, unsigned long len);

Description:
Perform "len" 32bit-wide write-cycles on the give addresses, with the the given data. The cycles will be good for non-brush-write flash devices (and possibly for other devices too). data[0] will be written on address[0], and data[len-1] will be written on address[len-1].

Arguments:
address:
Array of addresses to perform the writes on.

data:
Array of data to be written.

Notes:
The least significant byte of "data[n]" will be written in the byte at "address[n]", while the most significant byte will be written in the byte at "address[n]+3" (i.e. little-endian arrangement), regardless of the underline hardware (if the hardware is big-endian, this function must perform the conversion itself).

Method:
void flr32 (unsigned long address, unsigned long *data);

Description:
Perform a 32bit-wide read-cycle on the give address. The cycle will be good for non-brush-read flash devices (and possibly for other devices too).

Arguments:
address:
address to perform the read from.

data:
data read will be stored here.

Notes:
The contents of the byte at "address" will be stored in the least significant byte of "data", while the contents of the byte at "address+3" byte will be stored in the most significant byte of "data" (i.e. little-endian arrangement), regardless of the underlying hardware (if the hardware is big-endian, this function must perform the conversion itself).

Method:
void flr32n (unsigned long *address, unsigned long *data, unsigned long len);

Description:
Perform "len" 32bit-wide read-cycles on the give addresses. The cycles will be good for non-brush-read flash devices (and possibly for other devices too).

Arguments:
address:
Addresses to perform the read from.

data:
Data read will be stored here. Data read from "address[0]" will be stored in "data[0]", while data read from "address[len -1]" will be stored in "data[len - 1]".

Notes:
The contents of the byte at "address[n]" will be stored in the least significant byte of "data[n]", while the contents of the byte at "address[n]+3" will be stored in the most significant byte of "data[n]" (i.e. little-endian arrangement), regardless of the underlying hardware (if the hardware is big-endian, this function must perform the conversion itself).

It is obvious that, as new processor types are supported (i.e. PSMs are written for them), new methods will need to be defined, and the existing PSMs will have to be updated to implement these new methods (if possible). For example methods like 'flw16' will be needed if 16bit processors are to be supported, and these methods could possibly also be implemented by the existing PSMs (but possibly not).

So if you write a PSM and in the process you define some new methods, please send me your code (or even your suggestions for new useful methods) in order to incorporate them in the next versions of "ianjtag tools". This, of course, also stands for any new PSM, or other additions to the code, even if they do not include definitions of new methods

Flash Support Modules (files: fjtag.h, fjtag_xxx.c, fjtag_xxx.h)

Each flash support module exports a single function named "fjtag_xxx_init()", where "xxx" refers to the specific flash memory device and configuration supported by the module (e.g. "pjtag_isf2x16_init()" for an FSM that support Intel's StrataFlash chips in 2x16 configuration). This function initializes the module and fills-in an FSM interface structure, a pointer to which is supplied by the caller. Subsequent accesses to the module's services are performed only through this interface structure, which has been initialized and contains FSM variables, pointers to FSM variables, and function pointers. This initialization function is defined as follows:

Prototype:
int fjtag_xxx_init (struct fjtag_if_s *s, struct fjtag_param_s *p);

Description:
Detects the flash-device in use by reading its ID code, and initializes the flash support module. If the flash-device is not supported the function returns something negative.

Arguments:
s
this structured is filled-in by this function, and subsequently serves as the access point to the services of the FSM

p
this structure contain device specifications that cannot be determined by querying the device (e.g. memory size, or base address) and parameters that affect the module's behavior.

Returns:
non-negative, if the flash-device was detected and matched and the module was initialized successfully; negative otherwise.

Notes:
FSM users are supposed to call the "fjtag_xxx_init()" functions for every available FSM one after the other, until a call returns a non-negative value, which means that the flash-device has been detected and can be supported. From there-on the 's' structure can be used to access the services of the FSM regardless of which FSM this is.

An example may clarify things. Assume you have three FSM modules each exporting its own initialization function, say: "pjtag_isf2x16_init()", "fjtag_if2x16_init()", "pjtag_ibf2x16_init()". The way to go is to call these functions one after the other until one call succeeds like this:

    struct pjtag_if_s pjt;
    struct fjtag_if_s s;
    struct fjtag_param_s p;

    p.pjtag_if = pjt;    /* PSM already initialized */
    p.base_addr = 0x0;   /* device base address */
    p.size = 0x0;        /* find out yourself */
    p.progindevery = 1;  /* progress indicator update every 1 sec */
    p.progindrng = 1000; /* progress indicator goes form 0 to 1000 */
    p.progind = progind; /* progress indicator call-back */

    do {
        if ( fjtag_isf2x16_init(&s, &p) >= 0)
           break;
        if ( fjtag_if2x16_init(&s, &p) >= 0)
           break;
        if ( pjtag_ibf2x16_init(&s, &p) >= 0)
           break;
        printf("Fuck! Flash-device not supported!\n");
        exit(1);
    } while(0);
Of course you can devise more clever ways to code this, like having an array of pointers to initialization functions, and so on; but for our little example this will do. Now, even if you don't know which specific call succeeded (which you don't), you can access the module's services through the 's' structure like this:
     s->write(buff, 0, len, 1, NULL);
And that's the whole point of having the FSMs export only a single init function and providing the rest of their interfaces through a structure filled-in by init: It is something like having a simple application-level dynamic linking feature, or (if you 're into the OOP stuff) like having some sort of poor man's dynamic polymorphism.

The parameter structure taken as argument by every FSM init function is defined in "fjtag.h" like this:

  struct fjtag_param_s {
      struct pjtag_if_s *pjtag_if;
      unsigned long base_addr;
      unsigned long size;
      int progindevery;     
      unsigned long progindrng; 
      void (*progind)(void *, unsigned long, const char *);
  };
Its members serve the following purposes:

Member:
struct pjtag_if_s *pjtag_if;

Description:
Pointer to the PSM (initialized) interface structure.

Notes:
FSMs, obviously communicate with the flash-devices by calling PSM functions. So in order to initialize and use an FSM you have to have already initialized a PSM.

Member:
unsigned long base_addr;

Description:
Flash memory device base address

Member:
size

Description:
Flash memory device size (capacity) in bytes.

Notes:
If set to zero the FSM will try to figure it out by itself, probably making some assumptions (like that you have only one bank of ICs)

Member:
int progindevery

Description:
Progress indicator call-back will be called every this many seconds

Member:
int progindrng

Description:
Progress indication will run in the range form 0 to "progindrng"

Member:
void progind (void *arg, unsigned long p, const char *t);

Description:
Progress indicator call-back. This function will be called every time a progress indication update must be performed (i.e. every "progindevery" seconds)

Arguments:
arg:
Given to the FSM methods as argument, and passed-through to the progress indicator call-back. Not interpreted or used by the FSM.

p:
Position. How far has the process progressed. Ranges form 0 to "progindrng" (see member above)

t:
Operation title. Some processes may perform more than one lengthy operations. These operations are progress-indicated independently. This argument allows the progress indicator call-back to print a subtitle, or something, corresponding to the current operation.

Notes:
It is guaranteed by the FSM that the progress indicator call-back, will either not be called at all, or called at least twice: once with "p" equal to 0 and once with "p" equal to "progindrng".

The interface structure, which must be filled-in by every FSM init function is defined in the file "pjtag.h", and goes like this:

  struct fjtag_if_s {
      const char *name;          /* flash device name */
      int blocksz;               /* block size in bytes. 
                                    0 = variable-size blocks */
      int blocknr;               /* number of blocks */

      int *errnop;               /* current error-code (like errno(3)) */
      const char *(*strerror)(int); 
                                 /* returns err msg (like strerror(3)) */

      void (*read_mode)(void);   /* force the device to read-mode */

      int (*read)(void *, unsigned long, unsigned long, void *);
                                 /* read n bytes from flash to buffer */
      int (*write)(const void *, unsigned long, unsigned long,  
                   int, void *); /* write n bytes from buffer to flash */
      int (*verify)(const void *, unsigned long, unsigned long, 
                    void *);     /* compare n bytes of flash to buffer */
      int (*erase)(unsigned long, unsigned long,
                   void *);      /* erase given address range */
        int (*blockerase)(int, int, void *); 
                                 /* erase given block range */
  };
Follows a description of the structure's members. As before using the OOP terminology, I will call the non-callable members "attributes" and the callable members "methods".

Attribute:
const char *name;

Description:
String containing the name of the detected flash device.

Attribute:
int blocksz;

Description:
Size of the flash device's blocks in bytes.

Notes:
If the device has variable-sized blocks, "blocksz" reads as 0 and a new method for reporting the block sizes is required. If you write an FSM for such a device, please let me know how you dealt with this.

Attribute:
int blocknr;

Description:
Number of blocks in the device.

Attribute:
int *errnop;

Description:
Pointer to the code of the last error raised by the FSM, like errno(3)

Method:
const char *strerror(int errnum);

Description:
Returns the error-string associated with an error code, like strerror(3)

Arguments:
errnum:
Error-code to return the error-string of.

Returns: The error string associated with the error code.

Method:
void read_mode (void);

Description:
Force the device to read-mode, no matter what mode the device is currently in.

Method:
int read (void *buff, unsigned long addr, unsigned long len, void *progindA);

Description:
Read "len" bytes from the device, starting at address "addr" and store them in "buff".

Arguments:
buff:
Pointer to buffer to store the data read into.

addr:
Address to start reading from.

len:
Number of bytes to read.

progindA
Argument passed-through to the progress indicator.

Returns:
On success return non-negative. On error return negative and sets the "errno" variable (accessed through the "errnop" attribute) accordingly.

Method:
int write (void *buff, unsigned long addr, unsigned long len, int verify, void *progindA);

Description:
Write "len" bytes to the device, starting at address "addr". The bytes to be written are taken from "buff".

Arguments:
buff:
Pointer to the buffer where the bytes to be written are stored.

addr:
Address to start writing to.

len:
Number of bytes to write.

verify:
If non-zero verification will also be performed while writing.

progindA:
Argument passed-through to the progress indicator.

Returns:
On success returns non-negative. On error returns negative and sets the "errno" variable (accessed through the "errnop" attribute) accordingly.

Method:
int verify (void *buff, unsigned long addr, unsigned long len, void *progindA);

Description:
Verify (compare expecting to find equal) "len" bytes read from the device starting at address "addr", against the contents of "buff".

Arguments:
buff:
Pointer to the buffer keeping the data to verify against.

addr:
Address to start reading form.

len:
Number of bytes to read and verify.

progindA:
Argument passed-through to the progress indicator.

Returns:
On success returns non-negative. On error returns negative and sets the "errno" variable (accessed through the "errnop" attribute) accordingly.

Method:
int erase (unsigned long addr, unsigned long len, void *progindA);

Description:
Erase "len" bytes starting form "addr"

Arguments:
addr:
Address to start erasing from.

len:
Number of bytes to erase.

progindA:
Argument passed-through to the progress indicator.

Returns:
On success returns non-negative. On error returns negative and sets the "errno" variable (accessed through the "errnop" attribute) accordingly.

Method:
int blockerase (int start, int nblocks, void *progindA);

Description:
Erase "nblocks" blocks starting form block "start".

Arguments:
start:
First block to erase (blocks are counted starting form 0).

nblocks:
Number of blocks to erase.

progindA:
Argument passed-through to the progress indicator.

Returns:
On success return non-negative. On error return negative and sets the "errno" variable (accessed through the "errnop" attribute) accordingly.

Porting to different host-systems, supporting other hardware interfaces, and supporting additional target systems

Re-write the following functions in "jtag.c" so that they can work on your system and support your hardware interface:
  int jtag_init (void *opts);
  static inline void rstp (void);
  static inline int putp(int tdi, int tms);
The functions "io_access_on()", "io_access_off()", and "locate_port()" (all in "jtag.c"), which are only called by "jtag_init()" may no longer be necessary, so you might want to remove them also.

The FSMs have some minor dependencies on the Unix time(2) function, for calling the progress indicators at proper intervals, and for timing timeouts. You may need to address them also, which should normally be a trivial task (even if you have nothing like time(2)).

The user-interface code (i.e. "ianjflash.c" and "ianjpins.c") have plenty of Unix dependencies. If you are porting to a different O/S, you will probably have to rewrite them also.

If you are planning to support a new target system (i.e. new processor architecture, or different flash-device) you have to:

Remember to send your additions and modifications back to me, so that I can incorporate them in a future version of "ianjtag tools".

If you have any problem using or porting "ianjtag tools" don't hesitate to contact me. My email address can be found at the top of this document.

Licensing

"ianjtag tools" copyright (c) 2001 inAccess Networks

"ianjtag tools" is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Download

The project's web-page is at the following URL:
http://www.inaccessnetworks.com/projects/ianjtag/
From there you can download the latest version or any other versions available. As of this writing the latest version is 1.1.

Send any questions or suggestions about "ianjtag tools" to one of its authors. The authors's names and addresses are at the top of this page.

Please also send any modifications, additions, or corrections you have made to the source-code, so that we can include them in future versions.

We would also like to know you opinion about "ianjtag tools", and hear about you experiences using, or extending it.

Changelog

There is also a ChangeLog file for "ianjtag tools" automatically generated from the CVS commit comments.

Additional information