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

11

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 aprendimos que los valores de "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 sí lo es '0'. Por lo tanto, se les debe asignar un valor "No me importa" para permitir más optimizaciones en la lógica que impulsa estas señales (es decir, no en el archivo de registro en sí).

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

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

Como se 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 en (others => '0')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;
Fritz
fuente
¿Podrías explicar en qué estás tratando de hacer write_addressy write_data? ¿Qué optimización esperas que tenga lugar?
fru1tbat
Espero que el ejemplo aclare lo que quiero lograr.
Fritz

Respuestas:

9

Se lo dejaré a un experto en LRM para que 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 se reduce como se esperaba cuando el resultado está predeterminado '-'( 'X'también funciona, por cierto). 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 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 quieras decir con "indefinido". 'X'es técnicamente "desconocido" 'U'es para señales no inicializadas, que ModelSim muestra como "X"representaciones hexadecimales. 'X'Sin embargo, 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 (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(), lo 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 que solo lo uses case?.

fru1tbat
fuente
6

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é. Me parece que su código es un buen ejemplo de cuándo sería significativo usarlo.

Sin embargo, hay un inconveniente que uno debe tener en cuenta: en la síntesis, las funciones que impulsan las salidas donde no están involucradas las funciones podrían 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 importa (por error), esto puede hacer que el error sea más difícil de encontrar.

Herramienta de apoyo

Al menos las siguientes herramientas aceptarán que no le 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 .: "Manual de referencia de Synplify")

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

Carl
fuente
1
Tuve otra aplicación de ese inconveniente. El código trabajó en la simulación, pero no en el FPGA, porque mi código parecía: if signal = '1' then a; else b; end if;. Lamentablemente el signalno era 1o 0pero -. Entonces, en la simulación, la elserama se ejecutó, pero en hardware -resultó ser una 1, así que la rama verdadera se ejecutó ...
Fritz
Sí, he tenido simulaciones de errores similares, pero la mayoría de las veces, en mi caso, hay 'U's, comunes al comienzo de las simulaciones, que se han utilizado para que elsese ejecute algún bloque de código. Sería maravilloso si de alguna manera se pudieran hacer condicionales para propagar 'U's, similar al comportamiento de expresiones booleanas concurrentes.
Carl
Después de encontrar ese error, me aseguré de escribir siempre algo así if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;. Y agregué lo siguiente a todos los registros y recuerdos: 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;. Más lo mismo para write_data. Juntos, eso debería detectar casi todos estos errores.
Fritz
Esa es una forma, pero eso es demasiado detallado para mí. Desearía esta posibilidad dentro del lenguaje VHDL.
Carl
1
Bueno, sí, VHDL es un poco detallado, pero esa es solo la forma de 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").
Fritz