Control de protocolo JTAG usando URJTAG

From Qi-Hardware
Jump to: navigation, search

Proyecto Final- Sistemas Embebidos

  • Eder Mauricio Abello 1
  • Jose Roberto Cuarán 1
  • Luis Armando De Oro 1
Prelogo

Contents

[edit] Calificación

Porcentaje Presentación 20% Informe 30% Funcionamiento 50%
1 Entrega 10% 0.5 0.5 0.5
2 Entrega 30% 1.8 1.8 1.8
3 Entrega 30% 3 3 5


4 Entrega 30% 5 5 5

[edit] Descripción del Proyecto

El estándar IEEE 1149.1 desarrollado por el grupo JTAG (Joint Test Action Group), define la arquitectura Boundary-Scan implementada para ICs digitales, la cual surgió en respuesta a la necesidad de realizar pruebas sobre circuitos impresos con una elevada densidad de componentes.

La arquitectura Boundary-Scan permite la excitación y la captura del estado de los pines de entrada y salida de cada módulo a través de un registro de corrimiento. Este registro está compuesto por celdas denominadas BSC (Boundary-Scan Cell), las conectadas a los pines de entrada y salida de cada uno de los componentes. Dependiendo de la funcionalidad, la BSC está en capacidad de brindar información sobre el estado de la señal asociada a cada pin.

El objetivo del proyecto es elaborar un interfaz que permita al usuario realizar pruebas del hardware implementado en la FPGA incluida en la SIE, utilizando el protocolo JTAG expuesto anteriormente. Esta interfaz permitirá la introducción de patrones de onda definidos por el usuario y la visualización de las señales resultantes en la pantalla provista por la tarjeta, sin la necesidad de recurrir a generadores de señales ni analizadores lógicos. Cabe resaltar que la rapidez con la cual se puedan generar y muestrear las señales está limitada por la naturaleza serial del registro “Boundary Scan” y por la lógica interna a la cual obedece, restringiendo el ancho de banda al cual se pueden visualizar las señales.

[edit] Introducción al protocolo JTAG

Como se mencionó en la sección anterior, el protocolo JTAG es un estándar desarrollado por la IEEE en los años 80, el cual permite ser utilizado como herramienta de depuración, programación y realización de pruebas de circuitos integrados.

La interfaz JTAG consiste de 4 o 5 pines especiales

  • TCK: Test Clock
  • TMS: Test Mode Select
  • TDI: Test Data In
  • TDO: Test Data Out
  • TRST: Test Reset (opcional)
Celdas para un pin bidireccional

[edit] TAP Controller

Cada Test Access Port (TAP) consiste de una máquina de estados (FSM); Se utilizan TCK y TMS para hacer evolucionar la FSM, TDI para escribir y TDO para leer datos.

Boundary Scan Interface Architecture
Tap Controller

[edit] Registros

Cada TAP tiene un único Instruction Register (IR) y dos o mas Data Registers (DR); IR selecciona el DR a usar.

Los DR's requeridos por el estándar son:

  • BYPASS: registro de un solo bit para deshabilitar el dispositivo
  • IDCODE: identifica el fabricante, el código de dispositivo y la revisión. Sirve para linkear el dispositivo con el correspondiente BSDL.
  • BSR: registro de Boundary Scan. Permite mover datos hacia y desde los pines del dispositivo.

[edit] Instrucciones

Las distintas instrucciones se seleccionan escribiendo un valor determinado en IR; El estándar define las siguientes instrucciones:

  • BYPASS: se conecta TDI y TDO a través del registro BYPASS para poder acceder a otros dispositivos en la cadena
  • IDCODE: se conecta TDI y TDO a través del registro IDCODE
  • EXTEST: se conecta TDI y TDO a través del registro BSR.
    • En capture-dr se samplea el estado de los pines de entrada
    • En shift-dr se escribe/lee nuevos valores a/desde el BSR
    • En update-dr se actualizan los valores a los pines de salida
  • PRELOAD: carga valores de test en el BSR antes de un EXTEST
  • SAMPLE: permite leer el valor de los pines del dispositivo en funcionamiento. A veces se combina con PRELOAD.

Hay otras instrucciones definidas por el estándar como opcionales:

  • CLAMP: una variante de BYPASS que además setea el valor de los pines de salida usando PRELOAD
  • HIGHZ: desactiva los pines de salida llevándolos a 'Z'
  • INTEST: utiliza los pines para test de la lógica interna del dispositivo
  • RUBINST: coloca al dispositivo en modo de auto-test
  • USERCODE: devuelve un código definido por el usuario, por ejemplo un código de versión en diseños con fpga.

[edit] Formatos de archivos importantes

[edit] Archivos BSDL

Boundary Scan Description Language (BSDL) es un subset de VHDL útil para describir como está implementado JTAG en un dispositivo determinado. Estos archivos permiten conocer:

  • Las instrucciones JTAG soportadas por el dispositivo
  • Los tamaños de los registros internos (IR y DR)
  • La estructura del BSR, entre otros.

Contienen las siguientes partes:

  • Entity description: nombre del dispositivo y descripción de su funcionalidad
  • Generic Parameter: por ejemplo el tipo de encapsulado, etc.
  • Port description: descripción de los pines (input, output, bidireccional)
  • Use Statements: por ejemplo versión del estándar soportado
  • Pin Mappings: conecta señales lógicas internas a pines
  • Scan Port Identification: define los pines usados para JTAG
  • Instruction Register: describe el número de bits del IR
  • Register Access: detalla que registro se conecta entre TDI y TDO con cada instrucción
  • BSR description: descripción de los bits que componen el BSR

[edit] Archivos SVF

Serial Vector Format (SVF) fue desarrollado para representar patrones JTAG en archivos ASCII; Algunos de los comandos SVF mas comunes son:

  • SIR: realiza un shift sobre el IR. Modifica la instrucción
  • SDR: realiza un shift sobre DR. Escribe, lee y compara el valor del DR seleccionado
  • RUNTEST: fuerza a que se ejecute el estado run por un número especificado de clocks o un tiempo determinado
  • FREQUENCY: especifica la frecuencia máxima del TCK
  • TRST: controla la línea opcional TRST.

[edit] UrJtag

Es una herramienta de propósito general para acceso a dispositivos JTAG; Algunas de sus características son:

  • UrJtag es software libre y es activamente mantenida
  • Funciona bajo Windows o linux como una aplicación de línea de comandos
  • Soporta múltiples cables (USB, paralelo) y nuevos drivers pueden ser agregados
  • Para la definición de dispositivos utiliza archivos BSDL
  • Permite implementar SVF's Player; sin embargo, no todos los comandos SVF están soportados
  • Se ha usado con éxito en la programación de múltiples microcontroladores y FPGAs.

[edit] URJTAG SIE

Para portar UrJtag en la plataforma SIE se debe emular un cable que establecezca la comunicación entre el procesador y la FPGA por medio del protocolo Jtag, en otras palabras, la elaboración de un driver que permita el control de un dispositivo externo (FPGA) por parte del procesador.

[edit] Estructura del driver

El driver para la emulación de cualquier cable de UrJtag debe contener una serie de funciones, las cuales se mencionan a continuación:

  • connect(), init() - Inicialización
  • done(), cable_free(), disconnect() - Cleaning up
  • set_frequency() - Establece la tasa de datos de la cadena Jtag
  • clock(), get_tdo(), transfer() - Actividades inmediatas de Jtag
  • flush() - Es usado internamente para llevar a cabo de forma efectiva las actividades de JTAG
  • help() - Define un texto de ayuda para el cable en cuestión

Una descripción detallada de estas funciones se encuentra en http://www.urjtag.org/book/_drivers.html

[edit] Emulación cable jz47xx

El driver que se implementó para la emulación del cable dentro del programa UrJTAG se llama jz47xx, el cual fue desarrollado por el Ingeniero Carlos Camargo, profesor de la Universidad Nacional de Colombia. Este driver realiza la conexión entre el procesador y la FPGA, mediante el uso del mapeo de memoria del los GPIO's disponibles en el procesador. Este mapeo se encuentra descrito en los archivos jz47xx_gpio.c y jz47xx_gpio.h, los cuales permiten escribir y leer sobre los GPIOs, mediante un apuntador que guarda las direcciones en memoria.

Siguiendo la estructura de los cables para UrJtag http://www.urjtag.org/book/_drivers.html, como puede observarse en jz47xx.c se implemetaron las siguientes funciones:

  •  jz47xx_clock( urj_cable_t *cable, int tms, int tdi, int n )
    Esta función actualiza los valores para tms y tdi, n-veces, alternando los valores de tck, generando asi una señal de reloj con periodo dependiente de la función interna del programa cable(wait).
  •  jz47xx_init( urj_cable_t *cable )
    Inicializa los GPIOs para tdi, tms, tck y tdo. Los GPIOs se mapean en la memoria virtual y se definen como entradas o salidas dependiendo de su naturaleza.
  •  jz47xx_get_tdo( urj_cable_t *cable )
    Retorna el valor actual de tdo.
  •  jz47xx_set_signal( urj_cable_t *cable, int mask, int val )
    Con un enmascaramiento asigna a las señales tdi, tms, tck, de acuerdo al valor actual de cada una de ellas, mediante la variable prev_sigs.
  •  jz47xx_connect( urj_cable_t *cable, const urj_param_t *params[] )
    Genera la rutina de conexión del cable.
  •  jz47xx_cable_free( urj_cable_t *cable )( cable_t *cable )
    Junto de disconnect constituye la rutina de desconexión.
  •  jz47xx_help( const char *cablename )
    Informa el comando para usar el activar el cable siejtag.

UrJtag ofrece la libreria generic.c (src/tap/cable) con una serie de funciones genéricas, las cuales se ajustan a una gran cantidad de cables implementados en UrJTAG. Entre estas funciones se destacan:

  •   generic_disconnect
  •   generic_set_frequency
  •   generic_transfer
  •   generic_get_signal
  •   generic_flush_one_by_one

[edit] La Instrucción INTEST

Luego de emular el cable para establecer comunicación entre SIE y la herramienta URJTAG, se busca enviar vectores de prueba a la FPGA para determinar los cambios de las salidas de acuerdo a las entradas, esto se realizará por medio de URJTAG, empleando la instrucción INTEST definida por el protocolo JTAG. Esta instrucción es la que interesa debido permite verificar la lógica con que esta programado el dispositivo.

Entonces, el primer paso es cerciorarse que la instrucción INTEST si está soportada por URJTAG para SIE, ya que las modificaciones realizadas para la emulación del cable pueden dañar el correcto funcionamiento del programa.

La instrucción intest permite conectar TDI y TDO mediante el registro Boundary Scan; durante su ejecución, no se altera la lógica interna del dispositivo; esto la convierte en una instrucción útil a la hora de probar el comportamiento de un determinado lenguaje HDL, pues estando cargado en la fpga por ejemplo, es posible definir el valor de todas las entradas asociadas a este (actuando sobre las señales de entrada de la cadena BS) y verificar las salidas (leyendo las señales de salida de la cadena BS). Se asemeja a una simulación, con la ventaja de que las pruebas se hacen sobre la lógica implementada y casi que en 'tiempo real'.

La estructura de esta instrucción se muestra a continuación:

  1. instruction intest
  2. shift ir
  3. dr 10101001111010111010100111101011101...//vector de prueba de 588 posiciones
  4. shift dr
  5. shift dr
  6. dr out

En la línea 3 se define la cadena "de prueba", con los valores deseados para los pines de entrada; en la última línea, UrJtag arroja la cadena boundary Scan con las entradas previamente asignadas y las salidas correspondientes. Nótese que para el desarrollo de dicha instrucción se hace necesario conocer la posición en la cadena boundary scan de cada una de las entradas y salidas asociadas a un determinado programa; Para ello son indispensables el archivo BSDL (Boundary scan description language) correspondiente al dispositivo en uso (la fpga XC3S500E_VQ100 para nuestro caso) y el archivo .CSV correspondiente al programa de prueba.

En el archivo BSDL para la FPGA que consta SIE se encuentra detallada la información de cada uno de los datos de la cadena boundary scan, la cual consta de 588 registros para esta FPGA. Esto se puede verificar en la parte concerniente a BOUNDARY_REGISTER del archivo BSDL.

El archivo .CSV (comma-separated values) es generado por Xilinx despues de la síntesis e implementación de un determinado proyecto y sigue la siguiente estructura:

  • La primera columna: Número del pin
  • La segunda columna: Nombre de Señal Asiganada al pin
  • La tercera columna: Como puede ser usado el pin (Input, Output ó Bidireccional)
  • La cuarta columna: Nombre del pin
  • La quinta columna: Dirección del pin (Como fué asignado: INPUT, OUTPUT ó BIDIR)

Las otras columnas, no interesan por el momento.

La prueba se resume entonces a identificar las entradas y salidas utilizadas (por medio del archivo .CSV) e identificar la posición correspondiente a cada una de ellas mediante el archivo BSDL, para luego generar los vectores de prueba deseados. La lectura de las salidas se realiza de forma análoga.

Hasta ahora se han descrito de manera general los pasos para llevar a cabo una “simulación” utilizando UrJtag. A continuación se describe de forma detallada y a nivel de registros el proceso desarrollado internamente en la FPGA.

Podemos partir analizando la estructura del script.txt el cual contiene comandos entendibles por UrJtag. Esta herramienta se encarga de convertir dichos comandos en cambios de TMS que junto a TCK permiten recorrer los diferentes estados del controlador TAP (interno en la FPGA y encargado de manejar el protocolo JTAG). UrJtag maneja además las señales de TDI y TDO, a través de las cuales envía o recibe un flujo serial de datos.

Cabe destacar que cada una de las instrucciones utilizadas en UrJtag dan lugar a la transición por varios estados del controlador TAP (ver gráfica TAP Controller), aunque las mismas y los estados tengan nombres parecidos. Así por ejemplo, con shift dr(en UrJtag) se logra ingresar al estado SHIFT-DR 588 veces (longitud del BSR), posteriormente saltará al estado EXIT1-DR, luego al estado UPDATE-DR y finalmente al estado RUN-TEST-IDLE en espera de una nueva instrucción o definición de nuevos valores para el registro BSR.

Cabe recordar de la arquitectura Boundary Scan que inicialmente se define una instrucción y de acuerdo a esta se conecta un determinado registro de datos entre TDI y TDO. Así por ejemplo, la instrucción intest los conecta por medio del BSR (Boundary Scan Register).

Para definir un determinado registro (ya sea el de instrucción o uno de datos) se utilizan registros de corrimiento y registros de almacenamiento. De esta forma es posible ingresar datos por TDI, correrlos tantas veces como sea la dimensión del registro y una vez definido, se “guardan” en el registro auxiliar de almacenamiento.

Los registros de corrimiento (también llamados registros de captura) están formados por flipflops tipo D, mientras que los registros de almacenamiento (también llamados de actualización), están constituidos por latches.

Registros de captura y actualización

Al ejecutar UrJtag y configurar el cable (jtag->cable siejtag->detect en SIE) se establece el control de las líneas TDI, TDO, TMS y TCK de la FPGA por parte de UrJtag. El estado actual del controlador TAP hasta ese momento es TEST-LOGIC-RESET. En este estado, la FPGA no se encuentra en estado de test, por lo tanto, puede llevar a cabo cualquier operación normal sin ningún problema.

En instruction intest + shift ir se carga la instrucción intest (000111) al registro de captura mediante corrimientos (shift ir). Una vez corridos todos los bits de instrucción, se actualiza el registro de actualización. La actualización ocurre en el estado UPDATE-IR. A partir de este estado, un decodificador de instrucción puede acceder a la instrucción actual cuantas veces lo necesite, para determinar el registro de datos al que hace referencia, en este caso, el registro BSR.

Una vez definida la instrucción se procede a establecer vectores de prueba. Para ello es importante tener en cuenta como están distribuidos los pines en la cadena Boundary Scan como también la dirección de los mismos.

En el archivo xc3s500e_vq100.bsd, asociado con la FPGA de SIE, se describe el tipo de pines que ésta presenta y como están distribuidos en la cadena Boundary Scan; en la parte de logical port description se ve que la mayoría de pines son de tipo bidireccional (inout) y adicionalmente, en la parte de Boundary Scan register description se tiene que cada uno de estos pines tiene asociados tres posiciones o bits en el BSR: un bit de entrada, un bit de salida y un bit de control. De este modo, cada pin bidireccional implica la necesidad de tres celdas, tal como se muestra en la figura.

Celdas para un pin bidireccional

Son tres celdas, una celda de entrada, una de salida y una de control, esta última, encargada de definir la dirección del pin I/O. Dichas celdas están formadas por flipflops, latches y multiplexores. Aunque externamente solo se aprecie un pin, este se encuentra conectado a la lógica interna de la fpga mediante tres señales diferentes: input (IOB.I), output (IOB.O) y output enable (IOB.T).

Las diferentes celdas del registro BSR son de tipo BC_2 (como se puede apreciar en el archivo .bsdl), cuya principal característica es que los flipflops de captura pueden tomar como entradas sus propias salidas.

Las señales TDI, SHIFT, CLOCK, UPDATE, INTEST y EXTEST son enviadas por el controlador TAP y son recibidas por cada una de las celdas del registro BSR. Nótese que en la figura anterior se muestran las tres celdas asociadas a un pin inout (PAD), no obstante, la estructura de la celda difiere para otro tipo de pines.

Se puede observar que cada flipflop del registro de captura puede tomar a su entrada tres posibles valores:

  • En el caso de un flipflop de una celda de entrada, este puede tomar ya sea un valor asignado serialmente por TDI, el valor actual de su salida Q o el valor actual del pin en cuestión (PAD).
  • En el caso de un flipflop de una celda de salida, este puede tomar ya sea un valor asignado serialmente por TDI, el valor actual de su salida Q o el valor actual de IOB.O.
  • En el caso de un flipflop de una celda de control, este puede tomar ya sea un valor asignado serialmente por TDI, el valor actual de su salida Q o el valor actual de IOB.T.

El valor que toma a la entrada cada flipflop lo determina un multiplexor dependiendo de la instrucción actual (intest o extest) y de la señal de corrimiento SHIFT.

La señal IOB.I puede tomar el valor del PAD o el valor de los registros de actualización. Esta última característica permite entre otras cosas que mediante la instrucción intest se corra un bit determinado hasta el flipflop de la celda de entrada, se actualice el latch de actualización con dicho valor y finalmente sea asignado a la señal IOB.I.

Por otra parte, el PAD puede estar en modo de entrada o salida, para lo cual la celda de control establece el modo del buffer triestado (0-modo normal, 1-alta impedancia). Cuando se habilita a dicho pin como salida, el valor tomado puede ser directamente IOB.O (en modo normal de operación de la fpga) o el valor presente en el latch de actualización de la celda de salida, previamente asignado mediante corrimientos. Esto último hace posible llevar a cabo la instrucción extest (se corre un bit determinado hasta el flipflop de la celda de salida, se actualice el latch de actualización con dicho valor y finalmente es asignado al PAD).

La señal de control del buffer triestado puede provenir de la lógica interna de la fpga mediante la señal IOB.T, o del latch de actualización de la celda de control, cuyo valor se debe haber definido previamente mediante la rutina de corrimiento y actualización.

[edit] Prueba Intest para un negador

En el siguiente ejemplo se muestra paso a paso la prueba de la lógica interna de la FPGA (un negador) haciendo uso de la instrucción Intest.

  1.  module negador(
  2.       		input wire a,
  3.       		output wire b
  4.                );
  5.       assign b=~(a);
  6.       endmodule

El archivo de simulación es el siguiente:

  1.       module negador_tb;
  2.           reg a=0;
  3.           initial begin
  4.               $dumpvars;
  5.               a=0;
  6.           #2000 $stop;
  7.           end
  8.           always #100 a=~(a);    
  9.           wire b;    
  10.           negador negador(a,b);            
  11.       endmodule

Con base en el archivo de simulación se generan los vectores de prueba. Veamos el flujo de datos para el primer vector de prueba asociado al negador en cuestión; en este vector, se envía un “0” a la entrada “a” y se espera un “1” como salida “b”. Estas señales han sido asignadas previamente a los pines P62 y P63 respectivamente.

El vector de prueba enviado es el siguiente:

….100110110110110011<011>01100100110110110....

donde los tres bits encerrados corresponden a las posiciones 508, 507 y 506 en la cadena Boundary Scan. Son las señales IOB.I, IOB.O e IOB.T respectivamente, asociadas al pin P62 (entrada “a”).

Como puede observarse se define a IOB.I (de P62) en estado bajo y para las 587 posiciones restantes del registro BSR (incluyendo a IOB.O e IOB.T) se envían ceros o unos dependiendo del tipo de señal. En las posiciones asociadas a señales de salida o control el valor puede ser arbitrario (puesto que en modo de prueba intest dichos valores no son tenidos en cuenta), no obstante, algunas señales son de tipo interno y requieren un valor determinado. Para facilitar la definición de este registro, se utiliza un registro por defecto (para la función Intest) y sobre este se editan únicamente los valores de las entradas, en este caso por ejemplo, la posición 508.

Una vez definido el vector de prueba se debe lograr mediante corrimientos que el bit 508 llegue hasta el flipflop de la celda de entrada correspondiente al pin P62, tal como lo muestran las siguientes gráficas.

shift dr
shifted

El corrimiento anterior se hace en el estado SHIFT-DR del controlador TAP; una vez el vector de prueba ha sido corrido 588 posiciones, el controlador pasa al estado EXIT1-DR donde el flipflop de la celda de entrada toma el valor de su salida actual (0), mientras que los flipflops de las celdas de salida y de control son refrescados con IOB.O e IOB.T de la lógica interna de la FPGA. Esto ocurre en un flanco de subida de reloj (lineas rojas de la figura siguiente); en el flanco de bajada (lineas azules) el TAP manda la señal UPDATE para refrescar los latches de actualización (estado UPDATE-DR). En este momento, la lógica interna de la FPGA recibe el valor de la señal “a” (IOB.I de P62).

update

Por la lógica del negador, se establece la señal de salida (IOB.O de P63) en 1; la señal de control (IOB.T) se establece en 0, indicando que P63 está en modo salida. Por otra parte, el flipflop de la celda de entrada toma el valor (X) que le haya sido arbitrariamente enviado en el vector de prueba, el cual no interesa en este caso puesto que P63 está definido como salida.

establecimiento IOB's para P63

Finalmente, para leer el valor de la salida se realiza nuevamente un corrimiento (shift dr). Dado que se tiene otros vectores de prueba, es óptimo que previo al corrimiento se defina el segundo vector de prueba, logrando de esta manera la obtención de los resultados asociados al primero y la carga del segundo de forma simultánea.

shift dr - resultado
shifted - resultado

En el vector de salida se puede leer tanto el valor “a” como el de “b” (mostrados en rojo); los tres bits <010> corresponden a las señales internas del pin P63. Los diferentes vectores de salida son guardados por UrJtag en el archivo register_out.txt. A partir de estos podemos representar de forma gráfica las formas de onda de las diferentes señales de "simulación".

[edit] Desarrollo de la Aplicación en QT

Una vez comprobado el funcionamiento de la instrucción INTEST se procede a elaborar una interfaz gráfica que permita la visualización de las entradas y salidas, conforme el archivo de simulación lo describe. Acciones como recorrer la pantalla en toda su extensión son importantes, pues es posible que la cantidad de entradas y salidas o la duración de la simulación no se ajusten exactamente a una "página". Dadas las limitaciones de mouse y teclado que presenta SIE, es un tanto dificil desarrollar una interfaz amigable y aprovechar todas las posibilidades que ofrece QT; No obstante, se puede ejercer control sobre dicha interfaz a través de instrucciones desde la linea de comandos, usando los puertos de entrada y salida del procesador Jz4745, o bien realizando una interfaz Xburst-FPGA controlada desde espacio de usuario.

Diagrama General del Proyecto

[edit] Otros cambios realizados a UrJtag

  • Para realizar una interpretación adecuada de los valores de los registros dados por urjtag en la cadena boundary scan, es decir, conocer los valores de los pines de interés implementados en el proyecto implementado por el usuario en la fpga, se realizó una modificación al archivo cmd_dr.c, el cuál puede ubicarse en la dirección scr/cmd/ de UrJtag. En él puede verse la linea 99 donde se verifica el valor de la variable dr, la cual cuando es 1, carga en la variable r el valor de los registros de la cadena boundary scan, cuando se ha ejecutado en el script el comando dr out.
  1.     if (dir){
  2.         r = dr->out;
  3.         fputs(urj_tap_register_get_string( r ), fp);
  4. 	fprintf(fp,"\n");
  5. 	}
  • Se modificó también el archivo parse.c, ubicado en la dirección src/global de UrJtag, el cual define en la variable MAXINPUTLINE el número máximo de caracteres que se pueden pasar en la consola de jtag. El cambio realizado fue darle un valor de 600, ya que la cadena boundary scan de la FPGA contiene 588 posiciones, más los caracteres del comando empleador en el script.
  1. #define MAXINPUTLINE 600        /* Maximum input line length */


[edit] Interfaz con los puertos de propósito general (GPIO's)

Al igual que cualquier periférico conectado al procesador, los puertos de propósito general son controlados a través de la lectura y escritura de registros mapeados dentro de la memoria virtual del procesador Jz4745. Debido a que la arquitectura MIPS utiliza registros de 32 bits, los GPIOs son agrupados en 4 grupos (A, B, C y D) que pueden contener hasta un máximo de 32 puertos c/u. Cada banco esta asociado a diferentes registros que permiten obtener el estado de cada uno de los GPIOs, y en general, programar cualquier función que el procesador Jz4745 describe en su manual de programación.

Cada uno de los 32 bits que se encuentran en los registros de control representan hasta 32 GPIOs que constituyen el banco de puertos. Si se conoce la posición del bit asociado a cada uno de ellos, se puede controlar sus operaciones de manera independiente. En el manual de programación del Jz4745 se encuentra descrita el grupo y la posición del bit asignado a cada uno de los GPIOs implementados en el JZ4745.

En el esquemático de la SIE se identifican los siguientes puertos para la conexión de las señales TDI, TDO, TMS y TCK correspondientes al circuito TAP del JTAG en la FPGA:

  • TDI: Grupo C, Bit 14
  • TMS: Grupo D, Bit 23
  • TDO: Grupo D, Bit 24
  • TCK: Grupo D, Bit 28

La siguiente imagen muestra un ejemplo del funcionamiento de los registros de control de los GPIO's dentro del procesador. El registro que se menciona a continuación es el registro PXPIN, el cual contiene el nivel lógico actual ("1" o "0") de cada uno de los pines asociados al banco de registros. Por ejemplo, para poder leer el estado del puerto TDO (es el único pin que se establece como entrada al procesador), debemos leer el registro correspondiente a la dirección 0x10010300 (PDPIN, banco D) y ubicarnos en el bit en la posición 24, tal como se muestra en la figura.

Ubicación de TDO dentro del registro PXPIN.

En el proyecto, el control los GPIO se realizó con ayuda de las funciones contenidas en las librerías "jz47xx_gpio.h" y "jz47xx_mmap.h". Esta librerías contienen diferentes procedimientos que permiten realizar el control de los GPIOs de una manera más automática, lo cual genera un nivel de abstracción mayor a la hora de controlar los puertos a lo largo del código. Para mayor información sobre las funciones que ese encuentran descritas en los archivos mencionados anteriormente, favor remitirse a siguiente página [1].

[edit] Interfaz con la FPGA

Debido a que por lo general las simulaciones son muy grandes para ser mostradas por completo en la pantalla de la SIE, resulta necesario realizar una interfaz que permita al usuario desplazarse a lo largo de la gráfica de una manera sencilla. Al principio el grupo planteó la posibilidad de realizar el desplazamiento por medio de caracteres escritos en consola, sin embargo, a petición del profesor tutor se decidió usar los 2 pulsadores que se encuentran implementados en la SIE. A diferencia de los GPIOs, los pulsadores se encuentran conectados a la FPGA y no al Jz4745, por lo cual se elaboró la interfaz XBurst-FPGA que se encuentra descrita en el enlace Xburst-FPGA Interface. Básicamente, se debe configurar el controlador de memoria externa EMC para que configure el banco 2 como una interfaz de memoria estática de 8 bits, lo cual permite visualizar a la FPGA como una memoria SRAM de 1MB.

Configuración de la FPGA como una memoria estática de 8 bits.

Debido a que la única función que se desempeñará en la FPGA es la de informar acerca del estado lógico de los pulsadores, solo es necesario implementar la parte del hardware correspondiente al ciclo de lectura. En la siguiente figura se muestra la arquitectura de la interfaz implementada en la FPGA para elaborar este proceso. Es importante aclarar que el archivo .bit correspondiente a la lectura de los pulsadores se carga después de realizar todas las pruebas necesarias sobre la cadena boundary scan, debido a que si se carga antes posiblemente generará errores en la toma de datos.

Para lograr esto se implementó el siguiente código en Verilog, el cuál realiza la interfaz entre los pulsadores pb2 y pb3 conectados

  • pulsa.v
  1. `timescale 1ns / 1ps
  2. module pulsa(sram_data, ncs, noe, pb2, pb3);
  3.   parameter     B = (7);
  4.  
  5.   input            ncs, noe;
  6.   inout [B:0]      sram_data;
  7.   input		   pb2;
  8.   input		   pb3;
  9.  
  10.   wire   [B:0] rdBus;
  11.   assign rdBus[B:0] = {6'b0, pb3, pb2};
  12.  
  13.  
  14.   // interefaz signals assignments
  15.   wire   T = ~noe | ncs;
  16.   assign sram_data = T?8'bZ:rdBus;
  17.  
  18. endmodule
  • pines.ucf
  1. NET pb3             LOC = "P30";
  2. NET pb2	            LOC = "P13";
  3.  
  4. #DATA BUS
  5. NET "sram_data<7>"  LOC = "P4";
  6. NET "sram_data<6>"  LOC = "P5";
  7. NET "sram_data<5>"  LOC = "P9";
  8. NET "sram_data<4>"  LOC = "P10";
  9. NET "sram_data<3>"  LOC = "P11";
  10. NET "sram_data<2>"  LOC = "P12";
  11. NET "sram_data<1>"  LOC = "P15";
  12. NET "sram_data<0>"  LOC = "P16";
  13.  
  14. NET "noe"   LOC = "P86";
  15. NET "ncs"   LOC = "P69";
Interfaz XBurst-FPGA utilizada para el control de los pulsadores.

Como se describe en el enlace "Xburst-FPGA Interface", la señal CS2 se activa en "0" (activo bajo) cuando se accede a una dirección comprendia dentro del rango 0x14000000 a 0x17FFFFFF, mientras que la señal RDWR se fija en "1" cuando requiere un ciclo de lectura. El buffer tri-state que se visualiza en la figura anterior es controlado mediante estas 2 señales, las cuales permiten tomar el control del bus de datos en caso de que la señal CS2 sea "0" y la señal RDWR sea "1". Cuando el procesador realiza un ciclo de escritura o el CS2 no se encuentra activo, el buffer tri-state se fija en alta impedancia y la FPGA se desacopla del bus de datos.

Es importante resaltar que la FPGA no tiene un decodificador de direcciones debido a que solo se está trabajando con 1 solo registro en la FPGA. Esta condición permite acceder a su valor leyendo cualquier registro mapeado en las direcciones de memoria asignados al segundo banco de memoria estática (0x14000000 a 0x17FFFFFF).

[edit] Funciones Principales

  • Función int bsdl_position(QString pin_number, QString status): Esta función recibe como parámetro el número del pin (por ejemplo, P38) y la dirección de la señal asociada (input, output3 o controlr), y se obtiene la posición dentro del registro boundary scan a través del archivo bsdl brindado por el usuario.
  • Función QString csv::pin_alias_status(QString signal): Este procedimiento recibe como parámetro el nombre de la señal y retorna el número del pin junto con su estado, separados por una coma.
  • Función void script():Esta función genera el script a partir de los archivos .vcd y .csv, los cuales son suministrados por el usuario durante la ejecución del programa. La función script() utiliza dentro de su procedimiento las funciones pin_alias_status y bsdl_position, las cuales se describieron anteriormente.
  • Función void csv::timerEvent(QTimerEvent*): Es una rutina que se ejecuta constantemente, la cual lee la información contenida en los pulsadores y actualiza la interfaz gráfica por medio de la función update().
  • Función void csv::result_out(): Guarda en registros los valores necesarios para realizar las formas de onda a partir del archivo "register_out.txt".

Para un mayor entendimiento de cada una de las funciones descritas anteriormente, el código fuente del proyecto se puede descargar en el siguiente link: [2].

[edit] Interfaz Gráfica

Para la parte de visualización se utiliza la clase QPaintEvent de QT; esta clase permite pintar sobre un widget las diferentes formas de ondas y refrescar la pantalla ante una señal de UPDATE, señal que para nuestro caso se envía cada vez que se presione uno de los pulsadores de desplazamiento. Para la generación de las formas de onda se guarda previamente los valores correspondientes a cada señal en vectores independientes y en un vector adicional se guardan los nombres de las diferentes señales. Ante una señal de desplazamiento horizontal o vertical, simplemente se selecciona un nuevo intervalo de graficación dentro de estos vectores. Adicionalmente, se imprime una linea de tiempo con el fin de facilitar el análisis de la simulación. Entre las funciones encargas de esta parte gráfica están: result_out() y las contenidas en la clase Widget_Display.

Representación gráfica de los vectores de salida en SIE

[edit] Tutorial de instalación y Ejemplo de aplicación

[edit] PATCH siejtag

En el siguiente enlace puede descargar los archivos fuentes de UrJtag here

Descomprimir el archivo en el directorio donde fué descargado:


 tar xzvf /ruta_del_archivo/urjtag-0.10.tar.gz


Descargue el archivo patch here y copielo en la carpeta previamente descomprimida.


 cp /ruta_archivo_patch/siejtag.patch /ruta_del_archivo/urjtag-0.10


Luego vaya a la carpeta descomprimida y aplique el patch


 cd /ruta_del_archivo/urjtag-0.10
 patch -p1 < siejtag.patch

[edit] Portando UrJtag a SIE

Vaya a la carpeta donde aplicó el patch y realice la cross-compilación, para ello ejecute

 export PATH=$PATH:/ruta openwrt-xburst/staging_dir/toolchain-mipsel_gcc-4.3.3+cs_uClibc-0.9.32/usr/bin && echo $PATH


Compilar archivos para obtener los ejecutables:

 make


Establezca conexión con SIE

 sudo ifconfig usb0 192.168.254.102 && ssh root@192.168.254.101


Abra una solapa y copie los archivos generados tras las cross-compilación en SIE

scp directorio_urjtag_PLD/src/app/jtag/jtag root@192.168.254.101:/usr/bin/
scp directorio_urjtag_PLD/src/app/bsdl2jtag/bsdl2jtag root@192.168.254.101:/usr/bin/

El primer comando es el ejecutable correspondiente a UrJtag y el segundo corresponde a una aplicación que genera a partir de los archivos bsdl de la fpga, un archivo necesario para que jtag pueda hacer el reconocimiento de los pines dentro de la cadena boundary scan.


[edit] Creación de archivos .bsd para UrJtag

Envíe a SIE el archivo .bsd de la FPGA xc3s500e_vq100.bsd y ejecute el siguiente comando:

bsdl2jtag xc3s500e_vq100.bsd xc3s500e_vq100

En la carpeta data ubicada en el directorio del programa se encuentran los archivos necesarios para la xc3s500e_vq100, por lo cual solo se requiere realizar el paso anterior si se desea añadir soporte para nuevas versiones. Para utilizar los archivos contenidos en la carpeta anterior, copie los archivos de la dirección /urjtag_PLD/data/ en el directorio de SIE /usr/local/share/urjtag/, para ello ejecute:

En PC:
scp -r directorio_urjtag_PLD/data/ root@192.168.254.101:/usr/local/share/
En SIE:
cd /usr/bin/local/share/
mv data urjtag

Para ejecutar UrJtag en SIE, escriba en consola:

 root@BenNanoNote:~# jtag

[edit] Portando SimuSie a SIE

Para ello, es necesario portar la aplicación a SIE, por lo cual toca realizar una cross-compilación al código subido en el anterior link. Esto se consigue siguiendo los siguiente pasos:

Descomprimir el archivo descagado:

tar xzvf /ruta_del_archivo/simu_sie.tar.gz

Entrar a la carpeta descomprimida:

cd /ruta_del_archivo/simu_sie.tar.gz

Agregar el path del toolchain:

export PATH=$PATH:directorio_usuario/openwrt-xburst/staging_dir/toolchain-mipsel_gcc-4.3.3+cs_uClibc-0.9.32/usr/bin && echo $PATH

Agregar el path para aplicaciones de qt:

export PATH=$PATH:directorio_usuario/openwrt-xburst/target-mipsel_uClibc-0.9.32/qt-everywhere-opensource-src-4.7.0/bin/qmake -spec directorio_usuario/openwrt-xburst/build_dir/target-mipsel_uClibc-0.9.32/qt-everywhere-opensource-src-4.7.0/mkspecs/qws/linux-openwrt-g++ -o Makefile 

colgado Compilar los archivos:

qmake & make

Luego mandar el ejecutable a SIE junto con el archivo xc3s500e_vq100.bsd a un mismo directorio:

scp simu_sie xc3s500e_vq100.bsd root@192.168.254.101:/root/ruta_de_usuario


[edit] Generación de Archivos .VCD

La interfaz con el usuario es bastante básica, el usuario solo debe pasar lo parámetros a la aplicación, los cuales corresponden a las direcciones de los archivos .vcd, .bit y .csv, los dos últimos son generados por ISE de Xilinx al momento de crear un proyecto con el cual se quiere programar la FPGA.

Antes te pasar a la ejecución de la aplicación anteriormente detallada, es necesario la creación de los archivos VCD Value Change Dump, estos archivos almacenan el valor de un conjunto de variables cada vez que se produce un cambio en ellas. Estos archivos son creados a partir de los testbench para la simulación de proyectos en los lenguajes de descripción de hadware HDL (Verilog ó VHDL).

-Verilog.

En verilog la herramienta Icarus Verilog, es de gran ayuda para realizar este proceso, para la instalacón de ésta, se puede seguir este link, donde se encuentran detallados los diferentes pasos para su correcta instalación instalación. here.

-VHDL.

Para VHDL se cuenta también con una buena herramienta que permite la generación del archivo .vcd, GHDL, es un software libre al igual que Iverilog, el proceso de instalación puede realizarce a través del siguiente link here.


Además, se hace necesaria la instalación de GTKWave, el cual es un graficador de simulaciones descritas en archivos .vcd, y en el cual está basada la visualización final del proyecto.

Luego de haber instalado las dos herramientas anteriores ya se está listo para pasar a la creación del archivo .vcd dependiendo del lenguaje de descripción de hadware preferido.

[edit] Ejemplo en Verilog

A continuación se muestra el código de la implementación de un negador en Verilog, junto con su archivo de simulación:

  • negador.v
  1. module negador(
  2. 		input wire a,
  3. 		output wire b
  4.     );
  5.  
  6. 		assign b=~(a);
  7. endmodule
  • negador_tb.v
  1. module negador_tb;
  2. 	reg a;
  3. 	wire b;	
  4. 	negador negador(a,b);	
  5. 	initial begin
  6. 		$dumpvars;
  7. 		a=0;
  8. 		#2000 $stop;
  9.    end
  10. 	always #100 a=~(a);			
  11. endmodule

Para el caso de verilog se ejecutan los siguientes comandos, partiendo de este tutorial. here.

.iverilog -o dir_objeto/negador_tb dirección_testbench/negador_tb.v direc_arc_verilog/negador.v
vvp /negador_tb -vcd

El archivo VCD se genera con el nombre dump.vcd. Para ver el diagrama de tiempos en GTKWave de la simulación, se ejecuta el siguiente comando:

gtkwave dump.vcd

[edit] Ejemplo en VHDL

A continuación se muestra el código en VHDL del mismo negador anterior, junto con su archivo de simulación.

  • negador.vhd
  1. library IEEE;
  2. use IEEE.STD_LOGIC_1164.ALL;
  3. --use IEEE.STD_LOGIC_ARITH.ALL;
  4. --use IEEE.STD_LOGIC_UNSIGNED.ALL;
  5.  
  6. ---- Uncomment the following library declaration if instantiating
  7. ---- any Xilinx primitives in this code.
  8. --library UNISIM;
  9. --use UNISIM.VComponents.all;
  10.  
  11. entity negador is
  12. port(
  13.    a: in std_logic;
  14. 	b: out std_logic
  15. );
  16. end negador;
  17.  
  18. architecture Behavioral of negador is
  19. begin
  20. b <= not a;
  21. end Behavioral;
  • test_negador.vhd
  1. library ieee;
  2. use ieee.std_logic_1164.all;
  3. entity test_negador is
  4. end test_negador;
  5. architecture behavioral of test_negador is
  6. component negador
  7. port (a: in std_logic;
  8.       b : out std_logic);
  9. end component;
  10. signal a : std_logic:='0';
  11. signal b : std_logic;
  12. begin
  13. NEG: negador port map (
  14. a => a,
  15. b => b);
  16. -- Generar una senal cuadrada
  17. -- por la entrada del inversor
  18. process
  19. begin -- process
  20. wait for 100 ns;
  21. a <= not a;
  22. end process;
  23. end behavioral;


En VHDL, se deben realizar los siguientes pasos:

ghdl -a --ieee=synopsys negador.vhd
ghdl -a --ieee=synopsys test_negador.vhd
ghdl -e --ieee=synopsys test_negador
./test_negador --vcd=test_negador.vcd --stop-time=2000ns

El archivo VCD se genera con el nombre test_negador.vcd. El diagrama de tiempos se visualiza con el siguiente comando:

gtkwave test_negador.vcd


[edit] Ejecutando SimuSie

La aplicación recibe como parámetros tres archivos necesarios para la ejecución del proyecto que se quiere implementar dentro de la FPGA, lo cual se logra enviando los diferentes vectores de prueba descritos en el testbench y ya decoficados en el archivo vcd.

Antes de ejecutar el programa, se debe copiar los archivos .bit, .csv y .vcd a la SIE (sin importar la dirección de destino).

scp *.bit *.csv *.vcd root@192.168.254.101:/ruta_de_usuario_en_SIE


A continuación se procede a ejecutar el programa en la consola de la SIE. Los parámetros que recibe la aplicación son el archivo el .bit, el .csv y el .vcd, y su ejecución se realiza de la siguiente manera (el orden de los parámetros no importa):

simu_sie dirección/archivo/csv dirección/archivo/vcd dirección/archivo/bit -qws -nomouse

EL programa a través de la aplicación en QT empieza a programar la fpga con xc3sprog, luego envía el script (vectores de prueba) al jtag, jtag escribe un archivo register_out.txt, donde se encuentra desplegada la cadena boundary_sacan para cada vector de prueba enviado y este es leído nuevamente en qt y desplegado en pantalla, como se ilustra en la siguiente figura.

[edit] Análisis de Costos del Proyecto

Tabla de análisis de costos



TIEMPO DE DESARROLLO DEL PROYECTO: 8 Semanas


CANTIDAD DE HORAS REQUERIDAS POR SEMANA: 8 Horas


Costo del Personal: $5.760.000

Costo del Equipo Básico: $400.000

Costo de las Depreciaciones: $150.000

Otros Costos: $100.000


COSTO DEL PROYECTO: $6.410.000

Personal tools
Namespaces
Variants
Actions
Navigation
interactive
Toolbox
Print/export