Detectar el número de dispositivos en una cadena JTAG es una operación básica de diagnosis. Una vez migrada y actualizada la librería FTCJTAG de FTDI, se realiza esta operación como primer programa.
Esta entrada utiliza la librería FTCJTAG, de la que se habla en el artículo: JTAG y FTDI (I). Librería FTCJTAG.
Detectar el número de dispositivos en una cadena JTAG
La longitud de instrucciones JTAG, así como tamaño de registros o número de pines entrada/salida varía de un dispositivo a otro. A priori, es necesario disponer del BSDL de cada dispositivo para recorrer apropiadamente su máquina de estados, pero existen algunos procedimientos estandarizados en la norma IEEE 1149.1 con los que obtener información.
En la norma se especifican las instrucciones mínimas que debe implementar la interfaz, entre las que se encuentra la instrucción BYPASS. Dicha instrucción se invoca cargando 1’s lógicos en el Instruction Register, independientemente de la longitud de este. Con FTCJTAG esto se consigue con el siguiente código:
WriteDataByteBuffer writeBuff = {0xFF}; JTAG_Write(ftHandle, TRUE, 256, &writeBuff, 65535, RUN_TEST_IDLE_STATE);
Se carga un número aleatorio de 1’s en los Instruction Register, en este caso son 256 pero podrían cargarse 1.000, 200.000, etc. En cualquier caso habría que asegurar que ningún registro queda por llenar.
Una vez la instrucción BYPASS se ha cargado, cada dispositivo se comportará como un registro de desplazamiento (biestable D). Cada bit cargado en el primer elemento de la cadena aparecerá en la salida del último tras n pulsos de reloj, donde n será el número de dispositivos.
// Fill DR register with 0s memset(writeBuff, 0x00, sizeof(writeBuff)); JTAG_Write(ftHandle, FALSE, 256, &writeBuff, 65535, RUN_TEST_IDLE_STATE); // Fill DR register with 1s memset(writeBuff, 0xFF, sizeof(writeBuff)); JTAG_WriteRead(ftHandle, FALSE, 256, &writeBuff, 65535, &readBuff, &bytesReceived, TEST_LOGIC_STATE);
Tras ejecutar el último código readBuff contendrá el resultado de dicha operación, solo queda contar el número de ceros antes del primer 1 para saber el número de dispositivos.
// Read how many 0s were shifted chain_devices = count_JTAG_Devices(&readBuff, bytesReceived); if (chain_devices == 0) { printf("No devices detected\r\n"); } else { printf("Detected %d devices\r\n", chain_devices); }
Donde la implementación de la función count_JTAG_Devices(…) es:
int count_JTAG_Devices(PReadDataByteBuffer dataBuffer, DWORD byteBufferSize) { int numDevices = 0; for (DWORD i = 0; i < byteBufferSize; i++) { if ((*dataBuffer)[i] == 0xFF) { break; } else { char tmp_data = (*dataBuffer)[i]; for (int j = 0; j < 8; j++) { if ((tmp_data & 1) == 0) { numDevices++; tmp_data >>= 1; } else { break; } } } } return numDevices; }
Leer el IDCODE de los dispositivos
Una vez se conoce el número de dispositivos, es interesante conocer el IDCODE de estos.
La instrucción ‘IDCODE‘ es opcional, es posible que el siguiente método no funcione si el fabricante ha decidido no implementar esta característica.
En la IEEE 1149.1 se especifica que el registro de datos de la interfaz JTAG debe cargarse con el IDCODE tras un reset, de igual forma que si se hubiese ejecutado tal instrucción. Dependiendo del dispositivo este reset podría realizarse de varias formas.
- Mientras la señal TMS se mantiene a ‘1’, dar 5 pulsos de reloj en TCK. Este método funcionará en cualquier dispositivo y asegura que la máquina de estados se encuentre en Test-Logic-Reset.
- Si está implementada la señal TRST colocar un ‘0’ en la misma.
- Resetear el integrado a través de su pin de reset u otro método concreto. Es importante diferenciar que el estado de reset de cualquier integrado puede diferir del estado homónimo de la interfaz JTAG y su máquina de estados. Este método no debería ser la primera opción, si bien podría utilizarse cuando la documentación habla específicamente de ello.
Con el JTAG en reset, solo queda leer el contenido del registro de datos.
// Print devices IDCODE for (int i = 0; i < chain_devices; i++) { ftStatus = JTAG_WriteRead(ftHandle, FALSE, 32, &writeBuff, 4, &readBuff, &bytesReceived, RUN_TEST_IDLE_STATE); if (ftStatus != FTC_SUCCESS) { FTCJTAG_error(ftStatus, ftHandle); } printf("IDCODE %d: 0x", i); for (DWORD i = 0; i < bytesReceived; i++) { printf("%02X", readBuff[bytesReceived - i - 1]); } printf("\r\n"); }
Ejecutar lo anterior en la tarjeta de desarrollo Basys 3 da el siguiente resultado.

En GitHub se encuentra el código utilizado en este ejemplo, el cual incluye la gestión de errores que se ha omitido aquí por claridad.