15 votos

¿Cómo puedo especificar señales "don't care" en VHDL?

En los cursos de Diseño Lógico todos aprendimos que es posible minimizar una función lógica, por ejemplo utilizando un mapa de Karnaugh o el Algoritmo Quine-McCluskey . También nos enteramos de que "No me importa" aumentan el potencial de minimización.

Por ejemplo, tome un archivo de registro. El write_address y write_data señales no importan realmente cuando el write_enable la señal es '0' . Por lo tanto, se les debe asignar un valor "Don't Care" para permitir más optimizaciones en la lógica que se conduciendo estas señales (es decir, no en el propio archivo de registro).

¿Cuál es la forma correcta de especificar estos valores "Don't Care" en VHDL para que la herramienta de síntesis tenga más espacio para posibles optimizaciones?


Hasta ahora he encontrado las siguientes cosas que podrían ser adecuadas. Pero no estoy muy seguro de cuáles son los pros y los contras de cada enfoque:

  • Simplemente no asignar la señal. Esto parece que podría funcionar. Sin embargo, he descubierto que no funciona cuando se quiere definir una "constante de no hacer nada" de algún record ya que las constantes de los registros tienen que estar completamente especificadas (al menos así me lo dice Modelsim).
  • El std_logic_1164 define el valor '-' -- Don't care para std_ulogic . Parece que es la opción semánticamente correcta para un "don't care" explícito, pero nunca he visto que se utilice en ningún sitio (excepto en el no relacionado VHDL-2008 case? construcciones).
  • Modelsim utiliza el valor 'X' para mostrar las señales no definidas. Sin embargo, no estoy seguro de que las herramientas de síntesis entiendan un 'X' -asignación como "no me importa".

Aquí hay un fragmento de código sobresimplificado para clarificar, donde he inicializado las señales de no cuidado con '-' .

Como puede ver, la señal control.reg_write_address puede tener 3 valores diferentes: "----" , instruction(11 downto 8); y instruction(3 downto 0); . Ahora esperaría que esto se sintetizara en un multiplexor de 2 entradas si '-' se interpreta como "no me importa". Si hubiera inicializado la señal con (others => '0') en lugar de '-' la herramienta tendría que generar un multiplexor de 3 entradas en su lugar.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

package mytypes is
    type control_signals_t is record
        write_enable  : std_logic;
        write_address : std_ulogic_vector(3 downto 0);
        read_address  : std_ulogic_vector(3 downto 0);
    end record;

    -- All members of this constant must be fully specified.
    -- So it's not possible to simply not assign a value.
    constant CONTROL_NOP : control_signals_t := (
        write_enable  => '0',
        write_address => (others => '-'),
        read_address  => (others => '-')
    );
end package;

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library cfx;
use cfx.mytypes.all;

entity control_unit is
    port(
        instruction : in  std_ulogic_vector(15 downto 0);
        write_data  : out std_ulogic_vector(15 downto 0);
        ctrl        : out control_signals_t
    );
end entity;

architecture rtl of control_unit is
begin
    decode_instruction : process(instruction) is
    begin
        -- Set sensible default values that do nothing.
        -- Especially all "write_enable" signals should be '0'.
        -- Everything else is mostly irrelevant (don't care).
        ctrl       <= CONTROL_NOP;
        write_data <= (others => '-');

        if instruction(15 downto 12) = "1100" then
            -- Load 8 bit of data into the register file
            ctrl.write_enable  <= '1';
            write_data         <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
            ctrl.write_address <= instruction(11 downto 8);
        elsif instruction(15 downto 8) = "11111001" then
            -- Load 4 bit of data into the register file
            write_data         <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
            ctrl.write_address <= instruction(3 downto 0);
        elsif instruction(15 downto 8) = "10110101" then
            -- Read from the register file. Don't use the write signals at all.
            ctrl.read_address <= instruction(3 downto 0);
        end if;
    end process;
end architecture;

0 votos

¿Podría explicar con más detalle lo que está tratando de hacer con write_address y write_data ? ¿Qué optimización espera que se produzca?

0 votos

Espero que el ejemplo deje más claro lo que quiero conseguir.

12voto

fru1tbat Puntos 417

Dejaré que un experto en LRM proporcione una respuesta más detallada, pero en resumen, su enfoque debería ser válido - Hice una prueba rápida con una versión reciente de Quartus, y maneja '-' como se supone - la lógica generada se reduce como es de esperar cuando la salida es por defecto '-' ( 'X' también funciona, por cierto). Más sobre los enfoques que has enumerado:

  • No asignar la señal no es realmente una opción para su ejemplo, por supuesto, si usted no quiere latches. Si se trata de un proceso con reloj, estás un poco mejor, pero aún así tendrás habilitaciones donde podrías no necesitarlas. Tal vez estoy perdiendo su intención aquí.

  • '-' como se ha señalado anteriormente, es probablemente la mejor opción, tanto por razones semánticas como prácticas.

  • Depende de lo que se entienda por "indefinido". 'X' es técnicamente "desconocido". 'U' es para las señales no inicializadas, que ModelSim muestra como "X" para las representaciones hexagonales. 'X' parece funcionar, sin embargo, como he señalado anteriormente.

Otra alternativa sería hacer la optimización usted mismo y eliminar un caso de ser probado explícitamente:

if instruction(15 downto 8) = "11111001" then
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 4)), 16));
else
  write_data <= std_ulogic_vector(resize(signed(instruction(7 downto 0)), 16));
end if;

Sin embargo, esto tiene importantes desventajas (sobre todo relacionadas con la claridad del código), y probablemente optaría por una solución más ideal.

Por cierto, '-' también se utiliza habitualmente con std_match() que consideraría usar para su decodificación, por ejemplo:

if std_match(instruction(15 downto 8), "1100----") then

Aunque en ese punto, probablemente sea mejor usar simplemente case? .

7voto

canen Puntos 234

En resumen: es VHDL legal y suele estar soportado por las herramientas de síntesis.

Sin embargo, es bastante infrecuente que se utilice. No sé realmente por qué. Tu código me parece un buen ejemplo de cuándo tendría sentido utilizarlo.

Sin embargo, hay un inconveniente que hay que tener en cuenta: en la síntesis, las funciones que conducen a las salidas en las que intervienen los don't care podrían ser diferentes entre las ejecuciones de síntesis. Esto hace que la síntesis sea menos determinista. Si las salidas que han sido definidas como don't care se utilizan (por error), esto puede hacer que el error sea más difícil de encontrar.

Apoyo a las herramientas

Al menos, las siguientes herramientas aceptarán don't cares y harán uso de las posibilidades de optimización:

  • Xilinx (ref.: "XST User Guide")
  • Altera (ref.: "Recommended HDL Coding Styles")
  • Synplify (ref.: "Manual de referencia de Synplify")

Xilinx y Altera tratarán '-' y 'X' como no le importa, Synplify tratará esos y además 'U' y 'W' (débil) como no le importa.

1 votos

Tuve otra aplicación de ese inconveniente. El código funcionaba en la simulación, pero no en la FPGA, porque mi código parecía: if signal = '1' then a; else b; end if; . Lamentablemente el signal no era 1 o 0 pero - . Así, en la simulación, el else se ejecutó, pero en el hardware el - resultó ser un 1 ...por lo que se ejecutó la rama verdadera...

0 votos

Sí, he tenido errores similares pasar simulación, pero más a menudo en mi caso hay el 'U' s, comunes en el inicio de las simulaciones, que se han utilizado dando lugar a algunas else bloque de código que se está ejecutando. Sería maravilloso si se pudiera hacer que los condicionales se propagaran de alguna manera 'U' s, similar al comportamiento de las expresiones booleanas concurrentes.

0 votos

Después de encontrar ese error me aseguré de siempre escribir algo como if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if; . Y he añadido lo siguiente a todos los registros y memorias: assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR; y if write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if; . Y lo mismo para write_data . En conjunto, esto debería detectar casi todos estos errores.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X