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

10

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 Quine – McCluskey algorithm . También aprendimos que los valores de "No importa" aumentan el potencial de minimización.

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

¿Cuál es la forma correcta de especificar dichos valores de "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 realmente seguro de cuáles son los pros y los contras de cada enfoque:

  • Simplemente no asignando la señal. Esto parece que podría funcionar. Sin embargo, descubrí que no funciona cuando se quiere definir una "no hacer nada constante" de algún tipo record , ya que las constantes de registro deben estar completamente especificadas (al menos, Modelsim me lo dice).
  • El paquete std_logic_1164 define el valor '-' -- Don't care para std_ulogic . Parece que es la opción semánticamente correcta para un explícito "no importa", pero nunca lo he visto usado en ninguna parte (excepto en las construcciones no relacionadas con VHDL-2008 case? ).
  • Modelsim usa el valor 'X' para mostrar señales no definidas. Sin embargo, no estoy seguro de si las herramientas de síntesis entienden una asignación explícita de 'X' como "no importa".

Aquí hay un fragmento de código simplificado para aclarar, donde he inicializado las señales de "no importa" 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 sintetice en un multiplexor de 2 entradas si '-' se interpreta como "no 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;
    
pregunta Fritz

2 respuestas

9

Lo dejaré a un experto en LRM para que proporcione 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 maneja '-' como se supone - la lógica generada se reduce como se esperaba cuando la salida se establece de forma predeterminada en '-' (por cierto, también funciona 'X' ). 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 así tendrá habilitaciones donde no las necesite. Quizás me esté perdiendo tu intención aquí.

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

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

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

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 (la mayoría 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() , lo que consideraría usar para su decodificación, por ejemplo:

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

Aunque en ese momento, probablemente estés mejor usando case? .

    
respondido por el fru1tbat
5

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

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

Sin embargo, hay un un inconveniente del que se debe tener en cuenta: en la síntesis, las funciones que impulsan las salidas en las que están involucrados no pueden ser diferentes entre las ejecuciones de síntesis. Esto hace que la síntesis sea menos determinista. Si se usan las salidas que se han definido como "No importa" (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 el no importa y utilizarán las posibilidades de optimización:

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

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

    
respondido por el Carl

Lea otras preguntas en las etiquetas