3. HERRAMIENTA PARA LA CODIFICACIÓN xHDL 3.1 Introducción El diseño del filtro en xHDL (hardware design language) permite acelerar el desarrollo final del sistema en una ASIC (application-specific integrated circuit) o en una FPGA (field programable gate array). Esta herramienta va a permitir generar el lenguaje de descripción hardware (HDL) asociado al filtro en punto fijo diseñado. Son estos HDLs (tales como VHDL, Verilog, etc…) los lenguajes usados por los diseñadores hardware para permitir el diseño del sistema a nivel hardware. Este proceso de conversión del diseño del filtro en punto fijo a un lenguaje de descripción hardware lo realiza MATLAB de manera casi inmediata y muy sencilla. Gracias a esta herramienta que simplifica enormemente la codificación del diseño en lenguaje xHDL, se puede dedicar más tiempo a otras fases del desarrollo del filtro. 3.2 Propiedades Como entrada a esta herramienta se necesita un filtro cuantificado, como los que se obtienen al utilizar la herramienta de diseño de filtros que se ha estudiado en el apartado anterior. Los filtros cuantizados deben tener alguno de los siguientes formatos: • • En punto fijo con signo o sin signo. En punto flotante de doble precisión. Esta herramienta sólo permite la implementación del filtro que posee una de las siguientes estructuras: • • • • • • • • FIR (finite impulse response) FIR antisimétricos FIR transpuestos FIR simétricos Respuesta impulsiva infinita IIR implementada usando SOS en forma directa I. IIR implementada usando SOS en forma directa I transpuesta. IIR implementada usando SOS en forma directa II IIR implementada usando SOS en forma directa II transpuesta. Como salida de la conversión del filtro en lenguaje hardware se obtienen varios ficheros, según se especifique en las propiedades de configuración de la herramienta. El lenguaje de descripción hardware utilizado en la conversión también se debe especificar al inicio del proceso. Si se escoge lenguaje ‘Verilog’, el resultado de la codificación será almacenado en un fichero con extensión ‘.v’, en cambio si es el lenguaje ‘VHDL’ el seleccionado, se obtendrá un fichero con extensión ’.vhd’. Por defecto se colocan los ficheros generados en el subdirectorio llamado ‘hdlsrc’ del directorio de trabajo en el que MATLAB se encuentre. MATLAB ofrece al usuario varias formas de trabajar con esta herramienta de codificación en lenguaje xHDL. Así se encuentra una interfaz gráfica a modo de ventanas que simplifica enormemente el proceso llamada ‘fdatool’. Ésta también permite llevar a cabo la cuantificación del filtro en punto fijo además de muchas cosas más. Otra forma es la habitual inserción de las órdenes a través de la línea de comandos de MATLAB. Así para generar el fichero resultado se teclea la funcione ‘generatehdl’. Otras opciones de configuración que se deben especificar antes de iniciar el proceso y por tanto a tener en cuenta antes de generar el código asociado al filtro se pasan a especificar en los siguientes subapartados. 3.2 1 Propiedades asociadas a las opciones de ‘reset’ - ‘Reset type’ Asíncrono o síncrono; determina el tipo de reset usado al generar el código HDL asociado a los registros. El usar uno u otro depende del tipo de tecnología que se vaya a usar para implementar el diseño en lenguaje HDL ( es decir si se busca obtener una ASIC o FPGA ) Por defecto su valor es asíncrono. - ‘Reset asserted level’ Valor activo para la señal reset de entrada; determina si el valor del reset debe ser ‘1’ o ‘0’ para activar el reseteo de todos los registros del filtro del diseño del filtro. Por defecto su valor es ‘1’. 3.2.2 Propiedades asociadas a los puertos En cuanto a las propiedades asociadas a los puertos del sistema aparecen: - - ‘Nombre asignado a los puertos del sistema’ Por defecto, se tiene que el codificador del diseño del filtro en xHDL asigna los siguientes nombres a los puertos del filtro implementado en hardware: - Puerto de entrada : filter_in - Puerto de salida : filter_out - Puerto del reloj : clk - Puerto habilitador del reloj : clk_enable - Puerto del reset : reset ‘Tipo aplicable a los datos con los que trabajan los puertos’ Los puertos son interfaces que se encargan de transformar los tipos de datos con los que se trabaja en la generación de código hardware a tipos de datos compatibles con tipos MATLAB. Por tanto cada puerto es definido como un tipo de dato permitido en código hardware. En cuanto a los tipos utilizados en la generación de código hardware se destacan los siguientes que serán los dados por defecto: - Std_logic : Transforma el dato recibido a un dato que corresponda con el estado lógico deseado. - Std_logic_vector : Un vector de caracteres del tipo ‘Std_logic’ definidos anteriormente. - Wire : Pasa la señal de entrada sin llevar a cabo sobre ella ningún procesamiento. Así la herramienta de codificación del diseño del filtro en lenguaje HDL declara el tipo de dato del puerto de entrada y salida del filtro a ‘std_logic_vector’ en VHDL y a tipo ‘wire’ al trabajar con Verilog. Este tipo de dato se puede modificar al trabajar con VHDL, pero no ocurre lo mismo con Verilog en donde el tipo esta fijado a ‘wire’ y no se puede cambiar. - ‘Add input register’ y ‘Add output register’ Estas dos opciones permiten añadir un registro extra de entrada o salida durante la generación del código HDL. Esto es útil cuando la aplicación presenta requerimientos de tiempo, de manera que sea necesario añadir una latencia extra, por ejemplo estableciendo que la salida se almacene en el registro de salida cuanto un evento de reloj fijado tenga lugar y el reloj general del sistema tenga valor ‘1’. 3.2.3 HDL Propiedades avanzadas asociadas con la optimización del diseño Algunas de las propiedades de optimización pueden dar lugar a datos numéricos que difieran de los resultados obtenidos con el filtro cuantificado del que se partía para la generación del código HDL. - ‘Optimizing for HDL’ Por defecto, el codificador HDL produce un código que es compatible con los resultados numéricos obtenidos con el filtro cuantificado. Si se quiere obtener un código HDL que esté más optimizado en cuanto a velocidad de reloj o requisitos de espacio del diseño se activará esta propiedad, a costa de obtener resultados que difieran de los originales procedentes del filtro cuantificado ya que elimina operaciones extra de cuantificación. - ‘Coefficient Multipliers’ Especifica el tipo de procesamiento que sufren los coeficientes antes de la multiplicación; por defecto esta herramienta genera un código que contiene operaciones de multiplicación siendo el valor de esta propiedad es ‘multiplier’. Otros posibles valores serían ‘CSD’ y ‘factored-CSD’, estas dos últimas opciones permiten obtener una velocidad de reloj mayor y un área de diseño menor, ya que optimizan las multiplicaciones realizándolas mediante desplazamientos y sumas, pero se tendrá la posibilidad de obtener valores que difieran de los originales cuando ocurran situaciones de saturación o redondeo. - ‘FIR adder style’ Especifica el tipo de suma final que va a ser implementada en filtros FIR; por defecto se implementa un sumador lineal que es la opción vista y explicada en la mayor parte de los textos que versan sobre DSP. Otra opción posible para la implementación es la opción ‘pipeline’ o ‘tree’, que ofrece un velocidad de reloj mayor pero menos exactitud en la operaciones numéricas. Comparando las opciones se llega a que el número de sumas llevadas a cabo para la forma ‘lineal’ y ‘tree’ son las mismas, pero el tiempo que toma el llevarlas a cabo es menor con la opción ‘tree’ al presentar un procesamiento en paralelo y no secuencial como el que presenta la opción ‘lineal’. ‘Pipeline’ optimiza la frecuencia de reloj a costa de incrementar el tiempo de latencia del filtro ya que introduce un conjunto de registros adicionales. El modo ‘linear’ es el que asegura mayor exactitud numérica en comparación con el filtro original. Una vez fijadas todos los parámetros de configuración pertinentes según los resultados deseados, la generación del código asociado al filtro cuantificado es instantánea. 3.3 Ejemplo práctico Se muestra a continuación un ejemplo práctico con los resultados en lenguaje VHDL obtenidos de un filtro FIR cuantificado en punto fijo con los siguientes parámetros: Discrete-Time FIR Filter (real) -- ------------------------------Filter Structure : Direct-Form FIR Filter Order : 15 Stable : Yes Linear Phase : Yes (Type 1) Arithmetic : fixed CoeffWordLength : 16 CoeffAutoScale : true Signed : true InputWordLength : 16 InputFracLength : 15 OutputWordLength : 16 OutputMode : 'AvoidOverflow' ProductMode : 'FullPrecision' AccumMode : 'FullPrecision' RoundMode OverflowMode : 'convergent' : 'wrap' En cuanto a la configuración de la herramienta de generación de código en lenguaje xHDL, se empieza con la dada por defecto que asegura la máxima exactitud numérica del código HDL obtenido con respecto al filtro en punto fijo del que se parte. Los resultados obtenidos son los que a continuación aparecen: LIBRARY IEEE; USE IEEE.std_logic_1164.all; USE IEEE.numeric_std.ALL; ENTITY filini IS PORT( clk clk_enable reset filter_in filter_out ); : : : : : IN std_logic; IN std_logic; IN std_logic; IN std_logic_vector(15 DOWNTO 0); -- sfix16_En15 OUT std_logic_vector(15 DOWNTO 0) -- sfix16_En11 END filini; -----------------------------------------------------------------Module Architecture: filini ---------------------------------------------------------------ARCHITECTURE rtl OF filini IS -- Local Functions -- Type Definitions TYPE delay_pipeline_type IS ARRAY (NATURAL range <>) OF signed(15 DOWNTO 0); -sfix16_En15 -- Constants CONSTANT coeff1 CONSTANT coeff2 CONSTANT coeff3 CONSTANT coeff4 CONSTANT coeff5 CONSTANT coeff6 CONSTANT coeff7 CONSTANT coeff8 CONSTANT coeff9 CONSTANT coeff10 CONSTANT coeff11 CONSTANT coeff12 CONSTANT coeff13 CONSTANT coeff14 CONSTANT coeff15 CONSTANT coeff16 CONSTANT coeff17 -- Signals SIGNAL delay_pipeline SIGNAL product16 SIGNAL product14 SIGNAL product13 SIGNAL product12 SIGNAL product11 SIGNAL product10 SIGNAL product9 SIGNAL product8 SIGNAL product7 SIGNAL product6 SIGNAL product5 SIGNAL product4 SIGNAL product2 SIGNAL sum1 SIGNAL add_temp SIGNAL sum2 SIGNAL add_temp_1 SIGNAL sum3 SIGNAL add_temp_2 SIGNAL sum4 SIGNAL add_temp_3 SIGNAL sum5 SIGNAL add_temp_4 SIGNAL sum6 SIGNAL add_temp_5 SIGNAL sum7 SIGNAL add_temp_6 SIGNAL sum8 SIGNAL add_temp_7 SIGNAL sum9 SIGNAL add_temp_8 SIGNAL sum10 : signed(15 DOWNTO 0) := to_signed(0, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(-44, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(0, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(718, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(3071, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(7715, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(13975, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(19578, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(21845, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(19578, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(13975, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(7715, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(3071, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(718, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(0, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(-44, 16); -- sfix16_En17 : signed(15 DOWNTO 0) := to_signed(0, 16); -- sfix16_En17 : delay_pipeline_type(0 TO 16); -- sfix16_En15 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(31 DOWNTO 0); -- sfix32_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(32 DOWNTO 0); -- sfix33_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 SIGNAL add_temp_9 SIGNAL sum11 SIGNAL add_temp_10 SIGNAL sum12 SIGNAL add_temp_11 SIGNAL output_typeconvert SIGNAL output_register : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(36 DOWNTO 0); -- sfix37_En32 : signed(37 DOWNTO 0); -- sfix38_En32 : signed(15 DOWNTO 0); -- sfix16_En11 : signed(15 DOWNTO 0); -- sfix16_En11 BEGIN -- Block Statements Delay_Pipeline_process : PROCESS (clk, reset) BEGIN IF reset = '1' THEN delay_pipeline(0 TO 16) <= (OTHERS => (OTHERS => '0')); ELSIF clk'event AND clk = '1' THEN IF clk_enable = '1' THEN delay_pipeline(0) <= signed(filter_in); delay_pipeline(1 TO 16) <= delay_pipeline(0 TO 15); END IF; END IF; END PROCESS Delay_Pipeline_process; product16 <= delay_pipeline(15) * coeff16; product14 <= delay_pipeline(13) * coeff14; product13 <= delay_pipeline(12) * coeff13; product12 <= delay_pipeline(11) * coeff12; product11 <= delay_pipeline(10) * coeff11; product10 <= delay_pipeline(9) * coeff10; product9 <= delay_pipeline(8) * coeff9; product8 <= delay_pipeline(7) * coeff8; product7 <= delay_pipeline(6) * coeff7; product6 <= delay_pipeline(5) * coeff6; product5 <= delay_pipeline(4) * coeff5; product4 <= delay_pipeline(3) * coeff4; product2 <= delay_pipeline(1) * coeff2; add_temp <= resize(product2, 33) + resize(product4, 33); sum1 <= resize( add_temp, 37); add_temp_1 <= resize(sum1, 38) + resize(product5, 38); sum2 <= add_temp_1(36 DOWNTO 0); add_temp_2 <= resize(sum2, 38) + resize(product6, 38); sum3 <= add_temp_2(36 DOWNTO 0); add_temp_3 <= resize(sum3, 38) + resize(product7, 38); sum4 <= add_temp_3(36 DOWNTO 0); add_temp_4 <= resize(sum4, 38) + resize(product8, 38); sum5 <= add_temp_4(36 DOWNTO 0); add_temp_5 <= resize(sum5, 38) + resize(product9, 38); sum6 <= add_temp_5(36 DOWNTO 0); add_temp_6 <= resize(sum6, 38) + resize(product10, 38); sum7 <= add_temp_6(36 DOWNTO 0); add_temp_7 <= resize(sum7, 38) + resize(product11, 38); sum8 <= add_temp_7(36 DOWNTO 0); add_temp_8 <= resize(sum8, 38) + resize(product12, 38); sum9 <= add_temp_8(36 DOWNTO 0); add_temp_9 <= resize(sum9, 38) + resize(product13, 38); sum10 <= add_temp_9(36 DOWNTO 0); add_temp_10 <= resize(sum10, 38) + resize(product14, 38); sum11 <= add_temp_10(36 DOWNTO 0); add_temp_11 <= resize(sum11, 38) + resize(product16, 38); sum12 <= add_temp_11(36 DOWNTO 0); output_typeconvert <= resize( shift_right(sum12(36 DOWNTO 0) + ( "0" & (sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21) & NOT sum12(21))), 21), 16); Output_Register_process : PROCESS (clk, reset) BEGIN IF reset = '1' THEN output_register <= (OTHERS => '0'); ELSIF clk'event AND clk = '1' THEN IF clk_enable = '1' THEN output_register <= output_typeconvert; END IF; END IF; END PROCESS Output_Register_process; -- Assignment Statements filter_out <= std_logic_vector(output_register); END rtl; Una vez obtenido el código del filtro en lenguaje hardware de manera automática tras especificar los valores iniciales para las distintas propiedades de la herramienta, se pasa a hacer uso de un sintetizador, en este caso el ‘Symplicity-Pro’ es del que se hará uso, que ofrecerá una estimación de la cantidad de recursos hardware usados en la implementación del filtro, además de la frecuencia de reloj máxima que se consigue con el diseño. En este caso particular, trabajando con el filtro ‘filini’ y una ‘Virtex 2 XC2V1000’ de Xilinx, se obtienen los siguientes resultados: Cell usage: I/O Register bits: 0 Register bits not including I/Os: 272 (2%) Mapping Summary: Total LUTs: 1601 (15%) Process took 10.625 seconds realtime, 10.675 seconds cputime Estimated Frequency : 46.8 MHz Estimated Period : 21.366 3.4 Conclusiones capítulo 3 En este tercer capítulo se ha presentado la herramienta para la codificación del diseño del filtro en xHDL de MATLAB. Ha resultado ser una herramienta eficaz y sencilla para la codificación de diseños en lenguaje hardware. Resulta también un interfaz imprescindible que sirve de puente entre la herramienta de diseño de filtros en punto fijo y el sintetizador hardware que se va a usar en la implementación y que se ve con más detalle en el próximo capítulo 4 de aplicación.