4.2. Drivers

4.2.1. Cable-specific drivers (src/tap/cable)

Cable-specific drivers are those which are visible to the user through the "jtag" command shell. They’re listed in response to the "help cable" command. Each driver has to provide the following functions:

4.2.1.1. Initialization

After allocating a "cable_t" structure, a pointer to it and further parameters (as strings) have to be passed first to the selected cable’s connect() function.

Following that, the init() function is called via cable_init(). If cable_init() returns a zero value, all is fine and the cable is ready for use.

4.2.1.2. Cleaning up

There are two functions for actual cleanup:

  • done() is responsible for driving the hardware to a safe and consistent state.

  • cable_free() then can be used to clean up eventually extra allocated memory etc.

Both are usually called from chain_disconnect().

An additional mechanism allows to clean up if a disconnection was detected by the low level driver (e.g. USB or parallel port driver). A cable has to provide a disconnect() function for this purpose:

  1. Low level (e.g. parport) driver calls cable driver→disconnect()

  2. cable driver→disconnect() calls chain_disconnect()

  3. chain_disconnect() calls cable driver→done()

  4. chain_disconnect() then calls cable driver→cable_free()

After return from chain_disconnect() to cable driver→disconnect(), the cable_t structure has been freed and must not be accessed anymore.

4.2.1.3. JTAG Activities

Currently the API provides five different functions for performing operations at the JTAG interface on the low level signal level (using the four signals TMS, TCK, TDI, and TDO).

  • clock(tms,tdi,n) takes values for TMS and TDI output as its parameters, ensures that actual cable signals are set accordingly, and does a 0-1 transition on TCK (n times)

  • get_tdo() returns the current value at the TDO input.

  • set_trst(x) sets the TRST signal and returns the current value.

  • get_trst() returns the current value of the TRST signal.

For many JTAG adapters, there’s almost no delay when doing alternating clock() and get_tdo(). Writing and reading happens immediately and the result is available immediately as well. This is the case with most parallel port adapters (but not when attached to USB-to-parallel adapters or USB docking stations) and memory mapped IO (e.g. general purpose I/O pins of microcontrollers).

But there are adapters, especially USB and Ethernet based adapters, which exhibit a rather long delay between the initiation of reading a bit and the delivery of the value of the bit. It is at least 1 millisecond with USB, which would limit the transfer rate to 1 kHz. One way to workaround this is to transmit bits compacted into bytes and chunks of bytes, which is possible with the transfer() function.

  • transfer(in, out)

The transfer() function does a series of TCK pulses, with data for TDI read as bytes from memory. The bytes are automatically serialized. TMS is set to zero during transfer()s. Optionally, prior to each bit shifted out to the interface, TDO input can be read into memory (deserialized into a byte array of the same size as the input array).

It still doesn’t yield much improvement if the operation consists of many read and write transitions (e.g. repeatedly writing an instruction and some data register values, then reading from the data register, as it is necessary for memory access). For that reason, the above functions are also available in variants that don’t cause immediate activity, but rather schedule it for later. In the API, they’re visible as

  • cable_defer_clock()

  • cable_defer_get_tdo()

  • cable_defer_set_trst()

  • cable_defer_get_trst()

  • cable_defer_transfer()

These functions aren’t implemented in the cable driver (but currently in src/tap/cable.c). The cable driver just has to provide a flush() function to actually execute the queued activity in some cable-specific optimal way, and to store the results of get_tdo() and transfer() activity. The caller later can pick up the results using these functions (implemented in cable.c):

  • cable_get_tdo_late()

  • cable_get_trst_late()

  • cable_transfer_late()

As an example, consider the following sequence of activities:

  1. clock()

  2. get_tdo()

  3. clock()

  4. get_tdo()

If the result of the first get_tdo() isn’t absolutely required before the second clock(), the sequence can be optimized into the following sequence (if

  1. defer_clock()

  2. defer_clock()

  3. flush()

  4. get_tdo_late()

  5. get_tdo_late()

The next sections explain the queueing mechanism and its limits in detail.

4.2.1.4. When flushing occurs

The cable_flush() function is used to flush the queue towards the cable. It takes one additional argument, "how_much", which may be one of

  • OPTIONALLY: The cable driver may flush if it’s reasonable (e.g. if the queue has been filled so that some buffer limit for the cable interface is reached). It would be wise to flush early to keep the queue small, if there is no point in queueing up more items because the transfer to the cable would have to be split into smaller chunks anyway. This is used by UrJTAG immediately after adding items to the queue.

  • TO_OUTPUT: The cable driver should at least flush as much so that one output becomes available in the output queue. If there’s already something in the output queue, this should be interpreted similar to OPTIONALLY. This is used by UrJTAG immediately before it wants to use that output.

  • COMPLETELY: The cable driver has to flush the queue completely. This is used by UrJTAG immediately before actions that circumvent the queueing such as calls to the legacy clock/get_tdo functions. It could also be used by application code to ensure that some action is actually done in time.

4.2.1.5. JTAG activity queueing

The source in src/tap/cable.c provides to important functions to access the two queues "todo" (with activity to be done) and "done" (with results):

  • cable_add_queue_item

  • cable_get_queue_item

In src/tap/cable/generic.c you’ll find two implementations of dequeueing algorithms, i.e. implementations of the flush() function. These could be used by any new cable driver unless it provides a more sophisticated algorithm itself:

  • generic_flush_one_by_one() simply calls the "classic" functions one after another. The performance of the cable driver using this implementation will be the same whether the immediate or defer variants of the functions are used.

  • generic_flush_using_transfer() tries to optimize as many clock() and get_tdo() by transforming them into calls to transfer() instead. This can give a slight advantage.

The generic implementations also serve as a template for new cable-specific implementations.

4.2.1.6. Generic implementations

As a reference and in many cases completely sufficient for new cables, take a look at the code in src/tap/cable/generic.c, which contains generic routines, suitable for parallel port based cables (and some for other types of cables as well).

4.2.2. Link drivers

Link drivers like the "parport" driver collection provide the basis for communication between cable driver and actual JTAG adapter. The openwince JTAG tools supported only parallel port links with the "parport" drivers. UrJTAG introduced support for USB links, but in the early releases the drivers for these just mimic the parallel port links.

The basic functions provided by all link drivers are

4.2.2.1. parport

Currently there are parport drivers for direct access to the parallel port on a PC using I/O addresses (direct.c), and for using ppdev on Linux or ppi on FreeBSD.

In addition, there are "ftdi" and "ftd2xx" parport drivers that actually are for communication with USB cables based on FTDI chips. They cannot be used for connecting old parallel port cables through parallel to USB adapters with FTDI chips, and probably soon will be rewritten as "usbconn" drivers instead.

All parport drivers present a common API for setting and reading signals.

4.2.2.2. usbconn

The usbconn drivers provide a common API to search for and connect with USB devices. At the moment, there are drivers for libusd, libftdi and FTD2XX (e.g. to communicate with FTDI chip based cables through libftdi and/or FTD2XX, to communicate with Cypress FX2 using EZUSB.SYS or CyUSB.sys, and more).

4.2.3. Bus drivers

Bus drivers translate read and write operations on a bus into JTAG commands and methods. A bus in this context is neither restricted to a processor bus, nor to memory. Any system component that can be read from and written to could be seen as attached to a bus. I.e. external or internal memory (RAM, ROM, Flash) and peripherals connected to a processor or simply an FPGA with 1:1 connections.

The available bus drivers are listed in response to "help initbus". Each driver has to provide the following functions:

Important

Address parameters to the functions listed above specify always byte locations, independent of the actual data width. The bus driver has to adjust the address on its own if required.

4.2.3.1. Creation

Upon calling of its bus_new() function, the driver allocates a "bus_t" structure and performs all required internal initializations.

4.2.3.2. Initialization

After creation of the new "bus_t" structure, the bus_init() function will be called to give the driver the possibility to initialize it’s internal states or BSR bits as required. Such functionality has been split from bus_new() since some drivers require to re-initialize during runtime.

4.2.3.3. Cleaning up

The driver is supposed to free all allocated memory (including its "bus_t" structure). Additionally, it should set the device into a state that doesn’t prevent it from normal operation.

4.2.3.4. Short description

Prints a message describing the driver. This function is called by the "print" command before it lists the areas covered by this bus driver.

4.2.3.5. Preparation

This function is called whenever a bus operation is initiated. The driver should perform the required preparation steps so that subsequent calls to the bus_read_* and bus_write functions can perform their tasks properly.

E.g. a BSR bus driver would put the device into EXTEST mode to activate the boundary scan register on the device pins.

4.2.3.6. Description of the bus geometry

At certain stages, the bus driver’s bus_area() function is called by other commands to query the bus geometry for a given address. The bus driver must fill in the fields of a "bus_area_t" structure describing the geometry of the area in which the specified address is located:

  • a short textual description of the area

  • start address of area

  • length of area in bytes

  • data width in bits

Queries with an address out of range must result in an area length of

UINT64_C(0x100000000)

4.2.3.7. Initiate reading

Since the JTAG state machine defines a capture-shift-update sequence, it is required to shift the address for a read prior to capturing the read data. Therefore, the bus_read_start() function is called with the very first address to read from. This enables the driver to shift the address into the device before it can actually retrieve the read data for this address.

4.2.3.8. Read access

The bus_read_next() function fetches the read data from the device that has been addressed by a previous call to bus_read_start() or bus_read_next(). Again, this is due to the capture-shift-update sequence of JTAG:

  1. capture read data from device pins

  2. shift new address

  3. update new address to device pins

Important

The address parameter specifies the location of the following read access. It is not the address of the data returned by this function call.

4.2.3.9. Finish reading

Function "bus_read_end()" is called at the end of a read sequence. I.e. when the higher level command determines that the last data portion is to be read from the device. There is no new address and the function driver is supposed to return the read data that was addressed previously.

4.2.3.10. Atomic reading

For ease of use, a bus driver has to supply a "bus_read()" function that encapsulates reading data from a single address in an atomic operation. Bus drivers typically build this function from "bus_read_start()" and a subsequent "bus_read_end()".

4.2.3.11. Write access

This function writes one data element at the specified address. Since this translates to a single JTAG operation (capture ignored, shift and update address & data), there is no splitting as with the read functions.