DSP 101 Parte 3: Implementación de algoritmos en una plataforma de hardware

Hasta ahora hemos descrito la arquitectura física del procesador DSP, hemos explicado cómo el DSP puede ofrecer ciertas ventajas sobre los circuitos analógicos tradicionales y hemos examinado el filtrado digital, mostrando cómo la naturaleza programable del DSP se presta a esos algoritmos. Ahora examinamos el proceso de implementación de un algoritmo de filtro de respuesta al impulso finito (FIR) (introducido brevemente en la Parte 2, implementado en código ensamblador de la familia ADSP-2100) en una plataforma de hardware, el ADSP-2181 EZ-Kit Lite&tm;. La implementación se amplía para abordar los problemas de E/S de los datos.

Uso de filtros digitales

Muchas de las características arquitectónicas del DSP, como la capacidad de hacer bucles sin sobrecarga y de extraer dos valores de datos en un solo ciclo de procesador, serán útiles para implementar este filtro. En resumen, un filtro FIR es un filtro de ceros que se calcula mediante la convolución de una serie de puntos de datos de entrada con los coeficientes del filtro. Su ecuación de gobierno y su representación en forma directa se muestran en la figura 1.

Figura 1: Estructura del filtro FIR de forma directa.

En esta estructura, cada "z-1 la "caja" representa un único incremento del historial de datos de entrada en la notación de la transformación z. Cada una de las muestras retrasadas sucesivamente se multiplica por el valor del coeficiente adecuado, h(m)y los resultados, sumados, generan un único valor que representa la salida correspondiente a la enésima muestra de entrada. El número de elementos de retardo, o taps del filtro, y los valores de sus coeficientes determinan el rendimiento del filtro.

La estructura del filtro sugiere los elementos físicos necesarios para implementar computacionalmente este algoritmo mediante un DSP. Para el cálculo propiamente dicho, cada muestra de salida requiere un número de operaciones de multiplicación-acumulación igual a la longitud del filtro.

La línea de retardo para los datos de entrada y la lista de valores de los coeficientes requieren áreas de memoria dedicadas en el DSP para almacenar los valores de los datos y los coeficientes. La arquitectura Harvard mejorada del DSP permite a los programadores almacenar datos en la memoria de programa, así como en la memoria de datos, realizando así dos accesos simultáneos a la memoria en cada ciclo desde la SRAM interna del DSP. Con la memoria de datos que contiene las muestras entrantes y la memoria de programa que almacena los valores de los coeficientes, se puede recuperar un valor de datos y un valor de coeficiente en un solo ciclo para el cálculo.

Esta arquitectura DSP favorece a los programas que utilizan el buffering circular (que se discute brevemente en la Parte 2 y más adelante en este episodio). Esto implica que los punteros de dirección sólo tienen que inicializarse al inicio del programa y que el mecanismo de búfer circular garantiza que el puntero no salga de los límites de su búfer asignado, una capacidad muy utilizada en el código del filtro FIR para la línea de retardo de entrada y los coeficientes. Una vez determinados los elementos del programa, el siguiente paso es desarrollar el código fuente del DSP para implementar el algoritmo.

Desarrollo de software DSP

El flujo de desarrollo de software para la familia ADSP-2100 incluye los siguientes pasos: descripción de la arquitectura, generación del código fuente, validación del software (depuración) e implementación del hardware. La figura 2 muestra un ciclo de desarrollo típico.

Figura 2
Figura 2: Flujo de desarrollo del software.

Descripción de la arquitectura : En primer lugar, el usuario crea una descripción de software del sistema de hardware en el que se ejecuta el algoritmo. El archivo de descripción del sistema incluye toda la memoria disponible en el sistema y todos los dispositivos externos asignados a la memoria. A continuación se muestra un ejemplo de este proceso utilizando el ADSP-2181 EZ-Kit Lite.

Generación de código fuente : Al pasar de la teoría a la práctica, este paso -en el que una idea algorítmica se transforma en código que funciona en el DSP- suele ser el más largo del proceso. Hay varias formas de generar el código fuente. Algunos programadores prefieren codificar sus algoritmos en un lenguaje de alto nivel como el C; otros prefieren utilizar el lenguaje ensamblador nativo del procesador. Las implementaciones en C pueden ser más rápidas de desarrollar para el programador, pero el código DSP compilado carece de eficiencia al no aprovechar al máximo la arquitectura de un procesador.

El código ensamblador, al aprovechar al máximo el diseño de un procesador, produce implementaciones muy eficientes. Pero el programador debe familiarizarse con el lenguaje ensamblador nativo del procesador. La solución más eficaz es combinar C para las funciones de control del programa de alto nivel y código ensamblador para las partes del sistema que requieren mucho tiempo y matemáticas. En todos los casos, el programador debe conocer las limitaciones del sistema del procesador y las particularidades de los periféricos. El sistema de filtro FIR de ejemplo presentado en este artículo utiliza el lenguaje ensamblador nativo de la familia ADSP-2100.

Validación del software ("depuración") En esta fase se comprueban los resultados de la generación de código, utilizando una herramienta de software conocida como simulador- para controlar el flujo lógico del programa y verificar que un algoritmo funciona como se espera. El simulador es un modelo del DSP que a) proporciona visibilidad de todas las ubicaciones de memoria y registros del procesador, b) permite al usuario ejecutar el código del DSP de forma continua o una instrucción cada vez, y c) puede simular dispositivos externos que alimentan de datos al procesador.

Implementación del hardware: En este caso, el código se ejecuta en un DSP real, normalmente en varias fases: a) pruebas en una plataforma de evaluación como el EZ-Kit Lite; b) emulación en el circuito, y c) generación de la ROM de producción Probando permite determinar rápidamente si el programa funciona o no; esta técnica es el método de implementación utilizado en este trabajo Emulación en circuito supervisa la depuración del software en el sistema, donde una herramienta como EZ-ICE comprueba el funcionamiento del procesador en la plataforma de destino. Una vez terminada la depuración, un arrancar la ROM del código final puede ser generado; sirve como implementación final de producción.

Trabajar con el ADSP-2181 EZ-Kit Lite

Nuestro ciclo de desarrollo de ejemplo recorre el proceso, utilizando el ADSP-2181 EZ-Kit Lite (paquete de desarrollo ADDS-21xx- EZLITE) como hardware de destino para el algoritmo del filtro. El EZ-Kit Lite, una plataforma de demostración y desarrollo de bajo coste, consta de un procesador ADSP-2181 de 33 MHz, un códec de audio estéreo AD1847 y una EPROM con zócalo, que contiene el código de control para descargar nuevos algoritmos en el DSP a través de una conexión RS-232 (Figura 3).

Figura 3
Figura 3. Disposición de la tarjeta EZ-Kit Lite.

Para completar la fase de descripción de la arquitectura, hay que conocer la memoria y los dispositivos mapeados en memoria de que dispone el DSP. Los programadores registran esta información en un archivo de descripción del sistema para que el software de las herramientas de desarrollo pueda producir el código adecuado para el sistema de destino. El EZ-Kit Lite no necesita memoria externa al DSP, ya que la memoria disponible en el chip consiste en las 16.384 posiciones de memoria de programa (PM) SRAM del ADSP-2181 y las 16.352 posiciones de memoria de datos (DM) SRAM. (Las ubicaciones de 32 DM utilizadas para los registros de control del sistema no están disponibles para el código de trabajo). Puedes encontrar más información sobre el ADSP-2181, la arquitectura del EZ-Kit Lite y temas relacionados en los textos mencionados al final de este artículo.

La información sobre los recursos disponibles del sistema se almacena en un archivo de descripción del sistema para que lo utilice la familia de herramientas de desarrollo ADSP-2100. Un archivo de descripción del sistema tiene una extensión .SYS. La siguiente lista muestra un archivo de descripción del sistema [EZKIT_LT.SYS]:

.sistema EZ_LITE /* dar un nombre a este sistema */

.adsp2181 /* especifica el procesador */

.mmap0 /* especifica que el sistema arranca y */,

/* La posición PM 0 está en la memoria interna */

.seg/PM/RAM/ABS=0/código/datos int_pm[16384];

.seg/DM/RAM/ABS=0 int_dm[16352];

.endsys /* termina la descripción */

El listado declara 16.384 ubicaciones PM como RAM, empezando por la dirección 0, para permitir que se coloquen en ella tanto segmentos de código como valores de datos. También se declaran como RAM 16.352 posiciones de memoria de datos disponibles, empezando por la dirección 0. Como estos procesadores utilizan una arquitectura Harvard con dos espacios de memoria separados, la dirección 0 del PM está separada de la dirección 0 del DM. El códec ADSP-2181 EZ-Kit Lite está conectado al DSP a través de un puerto serie, que no está declarado en el archivo de descripción del sistema. Para que el archivo de descripción del sistema esté disponible para otras herramientas de software, la utilidad System Builder, BLD21, convierte el archivo .SYS en un archivo de arquitectura, o .ACH. El resultado de System Builder es un archivo llamado EZKIT_LT.ACH.

Tras escribir el código, el siguiente paso es generar un archivo ejecutable, es decir, transformar el código en instrucciones que el DSP pueda ejecutar. La primera ensamblar el código DSP. Esto convierte el archivo del programa en un formato que otras herramientas de desarrollo pueden manejar. El ensamblaje también comprueba que el código no tenga errores de sintaxis. A continuación, se enlaza el código para generar el ejecutable DSP, utilizando la memoria disponible que se declara en el archivo de arquitectura. El enlazador inserta todo el código y los datos del código fuente en el espacio de memoria; el resultado es un archivo ejecutable DSP, que se puede descargar en la placa EZ-Kit Lite.

Generar el código del filtro

Parte 2 de esta serie [Analog Dialogue 31-2, page 14, Figure 6] presentó una pequeña lista de código ensamblador para un filtro FIR. Aquí, ese código se ha ampliado para incorporar algunas funciones específicas del EZ-Kit Lite, como la inicialización del códec y la entrada/salida de datos. Los elementos básicos del algoritmo de filtrado (multiplicar-acumular, direccionar los datos mediante búferes circulares para los datos y los coeficientes, y confiar en la eficacia del bucle sin sobrecarga) no cambian.

Los datos entrantes se muestrean mediante el códec AD1847 incorporado, cuya frecuencia de muestreo, ganancia de entrada, atenuación de salida, selección de entrada y mezcla de entrada son programables. Su naturaleza programable hace que el sistema sea flexible, pero también añade una tarea de programación para inicializarlo para el sistema DSP.

Acceso a los datos

En este ejemplo, una serie de palabras de control para el códec -que se definirán al principio del programa en la primera sección del listado- lo inicializará para una frecuencia de muestreo de 8 kHz, con valores de ganancia moderados en cada uno de los canales de entrada. Como el AD1847 es programable, los usuarios suelen reutilizar los segmentos de código de interfaz e inicialización, cambiando sólo los valores de registro específicos para las distintas aplicaciones. Este ejemplo añadirá el segmento de filtro específico a un segmento de código existente en el software EZ-Kit Lite.

Este código de interfaz declara dos áreas de memoria que se utilizarán para la E/S de datos: "tx_buf", para los datos que se transmiten fuera del códec, y "rx_buf", donde se reciben los datos entrantes. Cada una de estas zonas de memoria, o buffers, contiene tres elementos, una palabra de control o de estado, datos del canal izquierdo y datos del canal derecho. En cada periodo de muestreo, el DSP recibe del códec una palabra de estado, datos del canal izquierdo y datos del canal derecho. En cada periodo de muestreo, el DSP debe proporcionar al códec un transmite palabra de control, datos del canal izquierdo y datos del canal derecho. En esta aplicación, la información de control enviada al códec no se modificará, por lo que la primera palabra del búfer de datos de transmisión se dejará como está. Supondremos que la fuente es un micrófono monoaural, que utiliza el canal derecho (no nos preocupamos por los datos del canal izquierdo).

Utilizando el programa shell de E/S que se encuentra en el software del EZ-Kit Lite, sólo tenemos que ocuparnos de la sección de código titulada "input_samples". Se accede a esta sección de código cuando se reciben nuevos datos del códec, listos para ser procesados. Si sólo se necesitan los datos del canal derecho, tenemos que leer los datos de la memoria de datos en la posición rx_buf + 2, y ponerlos en un registro de datos que se introducirá en el programa de filtrado.

Los datos que llegan del códec deben introducirse en el algoritmo de filtrado a través de la línea de retardo de entrada, utilizando la capacidad de almacenamiento circular del ADSP-2181. La longitud de la línea de retardo de entrada viene determinada por el número de coeficientes utilizados en el filtro. Como el búfer de datos es circular, el valor de datos más antiguo del búfer se encuentra donde apunta el puntero después del último acceso al filtro (Figura 4). Asimismo, los coeficientes, a los que se accede siempre en el mismo orden cada vez que se ejecuta el filtro, se colocan en un búfer circular en la memoria del programa.

Figura 4
Figura 4: Ejemplo de uso de buffers circulares para los datos de los filtros
entrada.

Código del algoritmo

Para operar con los datos recibidos, se puede utilizar la sección de código publicada en la última entrega con algunas modificaciones. Para poner en práctica este filtro, tenemos que utilizar la unidad de cálculo de multiplicación/acumulación (MAC) y los generadores de direcciones de datos.

La MAC del ADSP-2181 almacena el resultado en un registro de 40 bits (32 bits para el producto de dos palabras de 16 bits, y 8 bits para permitir que la suma se expanda sin desbordarse). Esto permite que los valores intermedios del filtro crezcan y se reduzcan según sea necesario sin corromper los datos. El segmento de código utilizado es genérico (es decir, puede utilizarse para cualquier longitud de filtro), por lo que los bits de salida adicionales del MAC permiten ejecutar filtros arbitrarios con datos desconocidos sin temor a perder datos.

Para implementar el filtro FIR, la operación de multiplicación/acumulación se repite para todas las tomas del filtro en cada punto de datos. Para ello (y estar preparado para el siguiente punto de datos), la instrucción MAC se escribe como un bucle. La capacidad de bucle sin coste del ADSP-21xx permite repetir la instrucción MAC durante un número determinado de cuentas sin intervención de la programación. Se establece un contador con el número de tomas menos uno, y el mecanismo de bucle disminuye automáticamente el contador para cada operación de bucle. Poner el contador del bucle en "taps-1" garantiza que los punteros de datos acaben en la ubicación correcta una vez finalizada la ejecución y permite que la operación final de MAC incluya el redondeo. Como el AD1847 es un códec de 16 bits, el MAC con redondeo proporciona un resultado estadísticamente insesgado redondeado al valor de 16 bits más cercano. Este resultado final se escribe en el códec.

Para una ejecución óptima del código, cada ciclo de instrucción debe realizar un cálculo matemático significativo. El ADSP-21xx lo consigue mediante instrucciones multifunción: el procesador puede realizar varias funciones en el mismo ciclo de instrucción. Para el código del filtro FIR, cada operación de multiplicación-acumulación (MAC) puede realizarse en paralelo con dos accesos de datos, uno desde la memoria de datos y otro desde la memoria de programa. Esta capacidad significa que en cada iteración del bucle se realiza una operación MAC. Al mismo tiempo, se recupera el siguiente valor de los datos y el coeficiente, y el contador se disminuye automáticamente. Todo ello sin perder tiempo en mantener los bucles.

A medida que se ejecuta el código del filtro para cada muestra de datos de entrada, la salida del bucle MAC se escribirá en el búfer de datos de salida, tx_buf. Aunque este programa sólo procesa datos de entrada de un solo canal, la salida se escribirá en ambos canales escribiendo en las direcciones de memoria intermedia tx_buf+1 y tx_buf+2.

El listado final del código fuente se muestra en la página 15. El algoritmo de filtrado propiamente dicho aparece en "Rutinas de servicio de interrupción". El resto del código se utiliza para la inicialización del códec y del DSP y la definición de las rutinas de servicio de interrupción. Estos temas se explorarán en futuros episodios de esta serie.

El EZ-Kit Lite

El software de monitorización de Windows suministrado con el EZ-Kit Lite, permite cargar un archivo ejecutable en el ADSP-2181 de la placa EZ-Kit Lite. Esto se hace a través del menú desplegable "Cargar", seleccionando "Descargar programa de usuario e ir" (Figura 5). Esto descargará el programa de filtrado en el ADSP-2181 e iniciará la ejecución del programa.

Figura 5
Figura 5: Menú de descarga del EZ-Kit Lite.

Revisión y resumen

El objetivo de este trabajo es presentar los pasos desde la descripción de un algoritmo hasta un programa ejecutable DSP que pueda ejecutarse en una plataforma de desarrollo de hardware. Los temas tratados incluyen el flujo de desarrollo del software, la descripción de la arquitectura, la generación del código fuente, la E/S de datos y la plataforma de hardware EZ-Kit Lite.

Hay muchos niveles de detalle asociados a cada uno de estos temas a los que este breve artículo no podría hacer justicia. Puedes encontrar información adicional en las referencias que aparecen a continuación. La serie continuará basándose en esta aplicación con temas adicionales. En el próximo artículo se analizarán con más detalle los problemas de entrada/salida de datos (E/S) a través de la estructura de interrupción del procesador, y se tratarán otras características del algoritmo del filtro simple.

Lista de códigos de filtros FIR para el EZ-Kit Lite

/**************************************************************

*

* hello81.dsp - archivo de plantilla para la placa 2181 ez-kit lite

*

* Este programa de ejemplo está organizado en las siguientes secciones:

*

* Montar las constantes de tiempo (system.h)

* Tabla de vectores de interrupción

* Inicialización del ADSP 2181 (init1847.dsp)

* Inicialización del códec ADSP 1847 (init1847.dsp)

* Rutinas de servicio de interrupción

*

* Este programa implementa un sencillo talk-through con el códec AD1847.

* Las rutinas de inicialización se han colocado en el archivo init1847.dsp. Este

* contiene la tabla de vectores de interrupción, el bucle principal ficticio y las rutinas de inicialización

* rutinas de servicio de interrupción para la recepción de pulsadores y del puerto serie 0.

* El botón pulsador (IRQE) conmuta el LED del EZ-Kit

* cada vez que se pulsa el botón.

*

* Los parámetros que controlan la frecuencia de muestreo, las ganancias, etc., son

* el archivo init1847.dsp. El puerto serie 0 se utiliza para comunicarse con el AD1847.

* Las interrupciones de transmisión se utilizan para configurar el códec, luego se

* las interrupciones de desactivación y recepción se utilizan para implementar el "talk-through"

* audio.

*

* Las definiciones de los registros de control mapeados en memoria están contenidas en

* el archivo: system.h

*

* La aplicación puede ser construida por :

*

* asm21 -c -l -2181 hola81

* asm21 -c -l -2181 init1847

* ld21 hello81 init1847 -a 2181 -e hello81 -g -x

*

**********************************************************/

.module/RAM/ABS=0 EzHello ;

#include

#define taps 255 /* longitud de la toma del filtro */

.var/dm/circ filt_data[taps]; /* búfer de datos de entrada */

.var/pm/circ filt_coeffs[taps]; /* buffer de coeficientes */

.init filt_coeffs: ; /* inicializar los coeficientes */

.externo rx_buf, tx_buf ;

.external init_cmds, stat_flag ;

.externo next_cmd, init_1847, init_system_regs, init_sport0 ;

/**********************************************************

* Tabla de vectores de interrupción

**********************************************************/

arranque; rti; rti; rti; /* 00: reinicio */

rti; rti; rti; rti; /* 04: IRQ2 */

rti; rti; rti; rti; /* 08: IRQL1 */

rti; rti; rti; rti; /* 0c: IRQL0 */

ar = dm(stat_flag); /* 10 : SPORT0 tx */

ar = pasar ar ;

si eq rti ;

omitir siguiente_cmd ;

saltar muestras_de_entrada; /* 14: SPORT1 rx */

rti; rti; rti ;

jump irqe; rti; rti; rti; /* 18: IRQE */

rti; rti; rti; rti; /* 1c: BDMA */

rti; rti; rti; rti; /* 20: SPORT1 tx o IRQ1 */

rti; rti; rti; rti; /* 24: SPORT1 rx o IRQ0 */

rti; rti; rti; rti; /* 28: temporizador */

rti; rti; rti; rti; /* 2c: apagado */

/************************************************************

* Inicialización del ADSP 2181

************************************************************/

empezar :

i0 = ^rx_buf; /* ¡¡recuerda que el autobufing del códec utiliza i0 e i1! */

l0 = %rx_buf ;

i1 = ^tx_buf ;

l1 = %tx_buf ;

i3 = ^init_cmds; /* i3 puede usarse para otra cosa después del init del códec */

l3 = %init_cmds ;

m0 = 0 ;

m1 = 1 ;

/* inicializar el puerto serie 0 para la comunicación con el códec AD1847 */

llamar a init_sport0 ;

/* Inicializa otros registros del sistema, etc. */

llama a init_system_regs ;

/* inicializar el códec AD1847 */

llamar a init_1847 ;

ifc = b#000000111111; /* borra las interrupciones pendientes */

nop; /* hay una latencia de 1 ciclo para ifc */

/* Establece los punteros para los datos y los coeficientes */

i2 = ^datos_de_inclinación ;

l2 = %filt_data ;

i5 = ^filt_coefs ;

m5 = 1 ;

l5 = %filt_coefs ;

imask=b#0000110000; /* activar la interrupción rx0 */

/* |||||||||+ | temporizador

||||||||+- | SPORT1 rec o IRQ0

|||||||+-- | SPORT1 trx o IRQ1

||||||+--- | BDMA

|||||+---- | IRQE

||||+----- | SPORT0 rec

|+------| SPORT0 trx

||+-------| IRQL0

|+--------| IRQL1

+---------| IRQ2

*/

/*----------------------------------------------------------------------

- esperar la interrupción y callar para siempre

----------------------------------------------------------------------*/

talkthru: inactivo;

salta a la vista;

/**************************************************************

* Rutinas del servicio de interrupción

**************************************************************/

/*----------------------------------------------------------------------

- Filtro FIR

----------------------------------------------------------------------*/

muestras de entrada :

ena sec_reg ; /* utiliza el banco de registros sombra */

ax0 = dm (rx_buf + 1); /* leer datos del convertidor */

dm(i2,m1) = ax0; /* escribir nuevos datos en la línea de retardo, puntero

ahora apunta a los datos más antiguos */

cntr = taps - 1 ;

mr = 0, mx0 = dm(i2,m1), my0 = pm(i5,m5); /* borrar acumulador, obtener primero

datos y el valor del coeficiente */

hacer filt_loop hasta esto; /* establecer un bucle sin sobrecarga */

filt_loop: mr = mr + mx0 * my0(ss), mx0 = dm(i2,m1), my0 = pm(i5,m5) ;

/* MAC y dos recuperaciones de datos */

mr = mr + mx0 * my0 (rnd); /* multiplicación final, redondear a 16 bits */

si mv saat mr ; /* comprueba el desbordamiento */

dm(tx_buf+1) = mr1 ;

dm(tx_buf+2) = mr1; /* datos de salida en ambos canales */

rti ;

.endmod ;

Ver también: (Parte 1) (Parte 2) (Parte 4)

Si quieres conocer otros artículos parecidos a DSP 101 Parte 3: Implementación de algoritmos en una plataforma de hardware puedes visitar la categoría Generalidades.

¡Más Contenido!

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir