¿Cómo puedo especificar señales de "no me importa" en VHDL?

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

Por ejemplo, tome un archivo de registro. Las señales write_addressy write_datarealmente no importan cuando la write_enableseñal es '0'. Por lo tanto, se les debe asignar un valor "No importa" para permitir más optimizaciones en la lógica que dirige estas señales (es decir, no en el archivo de registro en sí).

¿Cuál es la forma correcta de especificar tales valores "No importa" en VHDL para permitir 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, descubrí que no funciona cuando desea definir una "constante de no hacer nada" de algún recordtipo, ya que las constantes de registro deben especificarse completamente (al menos Modelsim me lo dice).
  • El std_logic_1164paquete define el valor '-' -- Don't carede std_ulogic. Parece que es la opción semánticamente correcta para un "no me importa" explícito, pero nunca lo he visto usado en ninguna parte (excepto en las case?construcciones VHDL-2008 no relacionadas).
  • Modelsim usa el valor 'X'para mostrar señales indefinidas. Sin embargo, no estoy seguro de si las herramientas de síntesis entienden una asignación explícita 'X'como "no me importa".

Aquí hay un fragmento de código muy simplificado para aclaración, donde he inicializado las señales de no importa con '-'.

Como puede ver, la señal control.reg_write_addresspuede tener 3 valores diferentes: "----", instruction(11 downto 8);y instruction(3 downto 0);. Ahora esperaría que esto se sintetice 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;
¿Podría dar más detalles sobre lo que está tratando de hacer con write_addressy write_data? ¿Qué optimización espera que se lleve a cabo?
Espero que el ejemplo deje más claro lo que quiero lograr.

Respuestas (2)

Dejaré que un experto en LRM brinde una respuesta más detallada, pero en resumen, su enfoque debería ser válido: realicé una prueba rápida con una versión reciente de Quartus y se maneja '-'como se supone que debe hacerlo: la lógica generada es reducida como se esperaba cuando la salida está predeterminada '-'( 'X'por cierto, también funciona). Más sobre los enfoques que enumeró:

  • No asignar la señal no es realmente una opción para su ejemplo, por supuesto, si no quiere pestillos. Si se trata de un proceso cronometrado, está un poco mejor, pero aún obtendrá habilitaciones donde quizás no las necesite. Tal vez me estoy perdiendo tu intención aquí.

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

  • Depende de lo que entiendas por "indefinido". 'X'es técnicamente "desconocido". 'U'es para señales no inicializadas, que ModelSim muestra como "X"para representaciones hexadecimales. 'X'Sin embargo, parece funcionar, como señalé anteriormente.

Otra alternativa sería realizar la optimización usted mismo y eliminar un caso para que no se pruebe 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 desventajas significativas (principalmente relacionadas con la claridad del código), y probablemente optaría por una solución más ideal.

Por cierto, '-'también se usa comúnmente 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 momento, probablemente sea mejor que solo uses case?.

En resumen: es VHDL legal y normalmente es compatible con herramientas de síntesis.

Sin embargo, es bastante raro verlo usado. Realmente no sé por qué. Me parece que su código es un buen ejemplo de cuándo sería significativo usarlo.

Sin embargo, hay un inconveniente que se debe tener en cuenta: en la síntesis, las funciones que impulsan las salidas en las que están involucradas las que no importan pueden ser diferentes entre las ejecuciones de síntesis. Esto hace que la síntesis sea menos determinista. Si se utilizan salidas que se han definido como no importantes (por error), esto puede hacer que el error sea más difícil de encontrar.

Soporte de herramientas

Al menos las siguientes herramientas aceptarán "no importa" y harán uso de las posibilidades de optimización:

  • Xilinx (ref.: "Guía del usuario de XST")
  • Altera (ref.: "Estilos de codificación HDL recomendados")
  • Synplify (ref.: "Manual de referencia de Synplify")

Xilinx y Altera tratarán '-'y 'X'no les importará, Synplify los tratará y, además , 'U'y 'W'(débil) no les importará.

Tuve otra aplicación de ese inconveniente. El código funcionó en simulación, pero no en FPGA, porque mi código se veía así: if signal = '1' then a; else b; end if;. Desafortunadamente el signalno fue 1o 0pero -. Entonces, en la simulación, elsese ejecutó la rama, pero en el hardware -resultó ser un 1, por lo que se ejecutó la verdadera rama...
Sí, he tenido una simulación de pase de errores similar, pero en mi caso, con mayor frecuencia, están las 'U's, comunes al comienzo de las simulaciones, que se han utilizado para elseque se ejecute algún bloque de código. Sería maravilloso si los condicionales pudieran hacerse de alguna manera para propagar 'U's, similar al comportamiento de las expresiones booleanas concurrentes.
Después de encontrar ese error, me aseguré de escribir siempreif signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if; algo como . Y agregué 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;. Además lo mismo para write_data. Juntos, eso debería detectar casi todos estos errores.
Esa es una manera, pero eso es demasiado detallado para mí. Desearía esta posibilidad dentro del lenguaje VHDL.
En su caso, preferiría conducir a 'X'("desconocido") en lugar de '-', o, mejor aún, propagar '-'en la entrada a '-'la salida y todo lo demás que no es o 0- eso es un poco más lógico. Verificar etc. en 's es una buena idea y lo hago a menudo. (La comprobación de addr/data when es un paso más, por supuesto). 1'X''X'write enablewrite enable='1'
Bueno, sí, VHDL es un poco detallado, pero así es VHDL. :D Por otro lado, también es muy explícito, y no hace "magia negra" a mis espaldas, lo cual me parece bastante agradable (cf. El Zen de Python "Explícito es mejor que implícito").