miércoles, 8 de junio de 2011

Paquetes: declaración y uso

Paquetes es una herramienta que provee VHDL para facilitar el uso de constantes, funciones, tipos, etc. que pueden ser compartidos entre diferentes componentes.
La sintaxis de la declaración y el cuerpo del paquete se detallan a continuación:

 1 package <package_name> is
 2         [subprograma_declarations];
 3         [constant_declarations];
 4         [type_declarations];
 5         [component_declarations];
 6         [attribute_declarations];
 7         [attribute_specifications];
 8 end <package_name>;
 9
10 package body <package_name> is
11         [subprogram_bodies];
12         [internal_subprogram_declarations];
13         [internal_constant_declaration];
14         [internal_type_declaration];
15 end <package_name>;

Como puede observarse en la parte declarativa SOLO las declaraciones son permitidas. Si es necesario la parte descriptiva de la declaración debe ser hecha en el cuerpo (body) del paquete.
En un blog anterior detallé la inferencia de una memoria ROM a partir del código genérico VHDL . En ese blog destacaba el uso de paquete para definir los valores de la ROM, sobre todo en casos que el tamaño de la memoria sea grande. En concreto, la declaración del paquete para este caso en particular sería la siguiente:

 1 library ieee;
 2 use ieee.std_logic_1164.all;
 3 use ieee.numeric_std.all;
 4
 5 package pkg_rom is
 6
 7  constant data_length : natural := 16;
 8  constant addr_length : natural := 10;
 9  constant mem_size    : natural := 2**addr_length;
10  subtype rom_word is std_logic_vector(data_length-1 downto 0);
11  type mem_type is array (mem_size-1 downto 0) of rom_word;
12
13  constant mem : mem_type :=
14        (0 => x"abcd", 1 => x"beef", 2 => x"5555",3 => x"1010",
15         4 => x"5a6b", 5 => x"f0f0", 6 => x"1234",7 => x"fabc",
16         8 => x"2345", 9 => x"9876", 10=> x"5432",11=> x"6666",
17        12 => x"0101",
18        13 => std_logic_vector(to_unsigned (1234,16)),
19        others => x"4247");
20
21 end package pkg_rom;

Como se ve es exactamente igual a las declaraciones realizadas en la parte declarativa de la arquitectura del componente sync_rom, detallado en el blog ROM.
Ahora, cómo se usa un paquete???? . . . . . .
En primer lugar el paquete en sí debe ser guardado con una extensión .vhd e importado en el proyecto que se está trabajando como si fuera un componente más del proyecto. En segundo lugar, debemos decirle a la herramienta de síntesis que se quiere hacer uso del paquete dentro de la sintesis del componente que se está sintetizando. En tercer lugar se usan las declaraciones del paquete dentro del componente como si  se hubieran declarado dentro del mismo componente. Mejor vemos todo esto siguiendo el ejemplo con el que estamos trabajando.
Las siguientes lineas de código detallan el uso del paquete previamente definido en un componente.

 1 library ieee;
 2 use ieee.std_logic_1164.all;
 3 use ieee.numeric_std.all;
 4 use work.pkg_rom.all;
 5
 6 entity sync_rom is
 7    port (
 8      clk     :in  std_logic;
 9      address :in  std_logic_vector(addr_length-1 downto 0);
10      data_out:out std_logic_vector(data_length-1 downto 0)
11           );
12 end sync_rom;
13
14 architecture synth of sync_rom is
15
16 begin
17    rom_proc : process (clk)
18    begin
19       if rising_edge(clk) then
20          data_out <=mem(to_integer(unsigned(address)));
21        end if;
22    end process rom_proc;
23
24 end architecture synth;

Analizando el código vemos que línea 4 es la que le dice a la herramienta de síntesis que existe un paquete, que es llamado pkg_rom, y que de ese paquete se usará todo (all). El uso de la librería work, se debe al hecho que es la librería que por defecto usan las herramientas de síntesis para buscar lo que no encuentran en el directorio de trabajo, esta librería en realidad es creada automáticamente por la herramienta de síntesis. En caso de tener una librería propia, creada por uno mismo, se debe reemplazar work por el nombre de la librería creada; en este caso la librería la debe crear uno mismo (concepto que abordaré más adelante en otro blog). Por otro lado, el uso de all, se refiere que se desea que la herramienta de síntesis compile e importe en el presente proyecto TODO, (all) lo que contiene el paquete. En casos de paquetes muuuy grandes, se puede reemplazar all por la función o declaración específica que se desea del paquete (de este modo se compila e importa solo lo que se necesita).
En el resto del código se hace uso de las constantes y tipos declaradas en el paquete sin ningún tipo de problemas.
Para completar lo explicado en este blog con respecto a paquetes, deberías ver el blog de funciones (functions) en el cual se detalla no solo la parte declarativa del paquete sino también la descripción funcional en el cuerpo del paquete (package body).
Bueno, espero haya sido de utilidad.... y a no tener miedo de usar paquetes !

5 comentarios:

  1. Hola, buenisimo tu blog. Seria interesante que escribas un articulo sobre como usar aritmetica de coma fija (y por que no tambien flotante), con la familia Xilinx.

    Intente seguir las instrucciones en http://www.vhdl.org/fphdl/ y cuando intente hacer la division, me encontre con que no andaba. Despues de releer el leame, encontre: "Did not like any of the fixed point division routines, had to comment them out". Muy piola. Me gustaria meterme en el tema de DSP, pero si no puedo dividir y multiplicar los coeficientes, no voy a poder hacer nada :P

    ResponderEliminar
  2. Tendré en cuenta tu pedido. Casualmente estoy preparando un curso DSP-FPGA. Por eso justamente ando medio ocupado, dame un tiempito y encontrarás acá algo al respecto....

    ResponderEliminar
  3. Buenisimo, me gustaria mucho ver algo de DSP. Me hice una placa con un codec AC97 y hasta ahora lo unico que hice fue una camara de eco, con buffer circular (Spartan-3 en la placa del starter kit). Te dejo la foto http://i.imgur.com/8U7Sw.jpg

    ResponderEliminar
  4. alguien podria explicarme como funciona la instruccion => std_logic_vector(to_unsigned (1234,16)), others => x"4247");

    ResponderEliminar
    Respuestas
    1. Estimado,
      En el ejemplo que describo lo que estoy haciendo es definiendo los valores a ser asignados en cada una de las direcciones de la memoria (en este caso en particular una ROM). Así a la dirección '0' le asigno el valor X"abcd", a la dirección '1' le asigno X"beef", y así sucesivamente,
      Ahora, supongamos que los valores a asignar en la ROM estén expresados como enteros. En este caso debo de alguna manera (calculadora, divisiones, etc) pasar de entero a hexadecimal. Esta 'conversión' la puedo hacer usando funciones y 'cast' de VHDL (ver http://hdl-fpga.blogspot.com.ar/2011/06/ieeenumericstdall-funciones-de.html), entonces tomo el valor entero '1234' y primero lo convierto a unsigned usando la función to_unsigned. Esta función de conversión de entero a unsigned necesita saber cuantos bits serán usado para representar '1234', por eso es el '16'. Una vez obtenido el valor en unsigned se hace un 'cast' a std_logic_vector para que de ese modo asignar el entero '1234' convertido a std_logic_vector a la posición 13 de la memoria.
      Por ultimo la instruccion 'others => X"4247", indica que el resto de los lugares de memoria sean asignados con el valor "4247".
      Espero haber sido claro.
      Gracias por tu pregunta !
      Exitos !

      Eliminar