domingo, 2 de enero de 2011

Implementacion FFT en FPGA Spartan 3A/3E


Este bloque denominado FFT realiza una transformada rápida de Fourier a partir de las muestras de la señal de entrada. Su implementación consiste en un CORE IP (Intellectual Property, Nucleo con Propiedad Intelectual) desarrollado por Xilinx Inc.

Un CORE IP consiste en una estructura que describe un componente cuyos parámetros de diseño pueden ser cambiados antes de la síntesis pero su código HDL no está disponible. Un CORE IP provee cierta flexibilidad pero está definido y optimizado para una familia específica de dispositivos programables.
La gran ventaja del uso de un CORE en el diseño sobre FPGA, además de que son diseños ya probados y depurados, es que aumenta en gran medida la rapidez de desarrollo de un prototipo, entregando en unos pocos pasos un componente  que puede tomar meses en implementarse desde cero. Por otra parte, la desventaja más significativa del uso de un CORE radica en que, si bien presenta una estructura flexible en cuanto a la configuración de muchas de sus características, existen parámetros que son fijos, por lo tanto el diseño tiene que adaptarse a estas condiciones. Además el estar sujetos a una licencia propietaria, limita mucho su uso en la mayoría de los diseños, y agregándole a esto el hecho de que estén atados a un dispositivo específico hace que no sea una opción poco utilizada en el desarrollo de proyectos sobre FPGA.
El CORE IP que se utiliza es el Fast Fourier Transform v7.1 propiedad de Xilinx Inc., el proceso para generar un CORE IP mediante el ISE CoreGenerator Tool se puede consultar en la web

La configuración elegida para el CORE IP de la FFT se muestra y se justifica a continuación.  

Al generar el CORE IP de la FFT aparece una ventana donde se seleccionan: el tamaño de la transformada, el reloj que utilizara, el tipo de arquitectura para procesar la mariposa. En la figura 2.9 se muestra la primera ventana de configuración. 


Aquí se fija el tamaño de la FFT en 1024 muestras.  El número de canales se fija en 1, este parámetro indica cuantas transformadas se llevarán a cabo al interior del bloque. El reloj de la transformada se establece en 25 MHZ para poder realizar el empalme con los demás componentes del sistema.

Se elige la arquitectura Radix-2 Burst I/O debido a las limitaciones de hardware con que cuenta el Spartan 3A. Dicha arquitectura procesa un arreglo de datos mediante la sucesión de pasos en los cuales el algoritmo efectúa mariposas de raíz-2 que recogen dos números complejos y regresa dos números complejos de mayor tamaño. Una arquitectura superior como Radix-4 Burst I/O implica un excesivo uso de multiplicadores y no quedarían recursos suficientes para el resto del sistema. Una arquitectura en Pipelined Streaming supera la cantidad de multiplicadores con los que cuenta la Spartan 3A así que sería imposible su implementación en esta FPGA. Por último, la arquitectura de Radix-2 Lite, Burst I/O presenta un pobre desempeño comparada con Radix-2 Burst I/O.

La siguiente ventana de configuración se presenta en la figura 2.10. En esta ventana se establece el tipo de dato para la entrada y salida de la FFT, la longitud de palabra para cada muestra, las opciones de escalado y redondeo, los pines adicionales, el orden de la salida y el tiempo de retardo para la lectura inicial de datos o tiempo de offset.


Se elige el tipo de dato como punto fijo pues las muestras de la señal de entrada provenientes del ADC y almacenadas en la memoria FIFO están en este formato en representación de complemento a dos.  El tamaño de cada muestra o longitud de palabra se fija en 14 bits debido a que el ADC entrega cada muestra en un vector de 14 bits.
En las opciones de escalamiento se escoge sin escalamiento, porque cuando se utiliza la opción de escala, se tiene una programación de escalamiento en un factor de 1, 2, 4 u 8 en cada etapa, si la escala es insuficiente se producirá un desbordamiento ya que la salida de la mariposa crecerá más allá del rango dinámico. Esta escala se aplica a cada etapa del algoritmo, lo que hace más lento el procesamiento de la FFT, y además no existe un método definitivo para el cálculo de esta escala. Igualmente, cuando se utiliza escalamiento, se realiza un redondeo en cada etapa de la mariposa y como se tiene que el número de etapas es log2N, si N es 1024 entonces serán 10 etapas, así que el redondeo introducirá un error en cada etapa, que irá aumentando en la etapa siguiente, disminuyendo la precisión de todo el sistema.

En los modos de redondeo se opta por redondeo convergente debido a que este hace un redondeo hacia abajo mientras que el truncamiento hace un recorte del exceso de bits, lo que se traduce en pérdida de información.

En la sección de pines opcionales se añade el pin CE o pin de habilitación de operación de la FFT, con el fin de evitar que la FFT realice operaciones en instantes no válidos para evitar el consumo innecesario de recursos. El puerto SCLR se habilita para permitir un reset síncrono de la FFT una vez se ha realizado la operación sobre un conjunto de muestras.

El ordenamiento de la salida se escoge en orden natural, porque no se quiere alterar la disposición con la que venía la señal a lo largo del sistema.

Por último, en input data timing, se selecciona un offset de tres ciclos de reloj para realizar una correcta sincronización entre la memoria FIFO y la entrada de carga de datos a la FFT.

En la última ventana de configuración del CORE FFT se escoge el tipo de memoria RAM que se utilizara para almacenar las muestras y se selecciona el método de optimización de resultados de la FFT. En la figura 2.10 se muestra este último paso de configuración de la FFT.

En el tipo de memoria se elige la opción de bloques de memoria RAM ya que ocupa menos área del FPGA y su síntesis requiere de menor tiempo. En las opciones de optimización se opta por usar cuatro multiplicadores para optimizar el rendimiento.


En la figura 2.12 se muestra un diagrama del CORE FFT generado. Aquí se observan las entradas a la izquierda y las salidas a la derecha.
A continuación se explican los puertos utilizados en este CORE. Es de resaltar que estos puertos generados son para este caso específico pues el bloque de la FFT genera los puertos dependiendo de las opciones de configuración utilizadas en la etapa de generación del CORE IP.
XN_RE: Es la entrada para la parte real
XN_IM: Es la entrada para la parte imaginaria, se utiliza cuando se quiere calcular la transformada inversa o IFFT, si se quiere una transformada directa se pasan ceros a esta entrada.
START: Sirve para dar comienzo a la transformada.
UNLOAD: Se establece en uno para indicar que se van a descargar los datos.
FWD_INV: Señal que indica el tipo de transformada a realizar, 1 para realizar una transformada directa y 0 para realizar una transformada inversa.
FWD_INV_WE: Habilita el uso de FWD_INV. Es activa en alto.
SCLR: Es activa en alto, sirve para realizar un reset síncrono sobre el bloque FFT.
CE: Habilita el reloj de la FFT, es activo en alto.
CLK: Reloj de la FFT.
XK_RE: Componente real de la transformada de Fourier.
XK_IM: Componente imaginaria de la transformada de Fourier.
XN_INDEX: Índice de los datos de entrada. Indica en que registro de la FFT está.
XK_INDEX: Índice de los datos de salida.
RFD: Read For Data. Active en alto, indica cuando la FFT está lista para que se carguen datos.
BUSY: Indica con un valor alto cuando la FFT está realizando alguna operación.
DV: Esta señal se pone en alto cuando existe un dato válido en la salida.
EDONE: señal de casi listo, se pone en alto un instante antes de que la operación de cálculo de la FFT haya sido realizada.
DONE: Se pone en alto para indicar que el cálculo de la transformada se ha completado.



Para realizar el control de operaciones y facilitar la interconexión con los demás componentes del sistema de análisis de señales de tipo Poliscopio, el CORE FFT se encapsula en un control que denominamos fft.  En la figura 2.13 se muestra el bloque de este control con las entradas en la izquierda y las salidas en la derecha.













A continuación se realiza una descripción de los puertos del bloque de control de la FFT. 

clk50: Este puerto es la entrada de reloj de 50MHZ de la FPGA.
iniciar_FFT: Este puerto de entrada recibe un valor alto cuando se requiere que la FFT entre en operación.
reset: Está conectado al reset del CORE FFT y cumple la misma función, reinicia el bloque completamente.
datos_re: Este puerto de entrada recibe el vector de datos proveniente de la memoria fifo.
rfd_fft: Está conectado a RFD del CORE FFT asi que en la salida pone un valor alto cuando el CORE FFT está listo para que se carguen datos.
dv_fft: Está conectado a DV del CORE FFT y pone un valor alto cuando se tienen datos válidos a la salida de la FFT.
xn_index_fft: Índice para la entrada. Índica la posición de la muestra de entrada en el que esta la FFT
xk_index_fft: Índice para la salida. Indica el numero de dato de salida en el que esta la FFT.
xk_re_fft: Salida parte real de la transformada de la FFT, es un vector en complemento a dos de 24 bits de longitud.
xk_im_fft: Salida parte imaginaria de la transformada de la FFT, es un vector en complemento a dos de 24 bits de longitud.

El funcionamiento de la FFT dentro del sistema de análisis de señales de tipo Poliscopio se explica mediante el diagrama mostrado en la figura 2.14. En este diagrama se han llamado las variables con una nomenclatura en letra minúscula debido a que no son los puertos del CORE FFT ni tampoco los puertos del bloque de control fft sino que son variables auxiliares de control para facilitar la asignación de estados, la escritura y lectura sobre los puertos de los componentes. Sin embargo las variables asociadas a un puerto llevan un nombre que permite relacionarlas con este.
Siguiendo el diagrama de la figura 2.14, el inicio de la operación de la fft se realiza poniendo ce_fft en alto para activar el reloj del bloque, las demás variables de control se ponen en cero. Luego de esto se verifica el estado de busy para confirmar que no se esté realizando ninguna operación sobre la FFT. Si la FFT está ocupada se espera a que se desocupe para continuar, cuando la FFT está libre se examina el estado de iniciar, se realiza un reset si se requiere que la FFT entre en operación, es decir si iniciar esta en uno. Si no es así no se avanza al siguiente estado.

Una vez se ha realizado el reset para garantizar que la FFT este vacía, se verifica que no esté ocupada mediante la variable busy, si la FFT no está ocupada, se fija en transformada directa el tipo de transformada a realizar utilizando las variables fwd_inv_fft en un valor alto y fwd_inv_we_fft en alto para habilitar la anterior.
Para continuar se verifica que la FFT no esté realizando alguna operación examinando el estado de la variable busy, si la FFT está libre se procede a cargar los datos de entrada, para esto se habilita el reloj de la FFT mediante la variable ce_fft puesta en un valor alto.
Una vez cargados los datos, se espera hasta que la señal edone tenga valor uno, es decir hasta que la operación de cálculo de la FFT este casi lista. Cuando se indica que la operación está casi lista, se indica al bloque FFT que se van a descargar los datos poniendo un uno en unload_fft. Para continuar, una vez más se verifica que busy valga cero. En la etapa ESPERAR se regresa unload_fft a nivel bajo y se espera a que dv_fft tome valor uno, es decir a que se tengan datos válidos listos para ser leídos, cuando esto sucede se procede a guardar los datos y por último se regresa a la etapa de inicio.
En cada etapa del proceso se verifica el estado de busy para poder continuar, debido a que esta señal pone uno lógico cuando la FFT está realizando una operación y si se orden alguna instrucción al bloque cuando este está ocupado, la instrucción no se ejecuta y se genera un error.
Una vez concluido el proceso los datos son guardados en una memoria RAM que se ubica a la salida del bloque FFT. Ambas componentes, la parte real y la parte imaginaria se concatenan para ser almacenadas en una sola memoria.

No hay comentarios:

Publicar un comentario