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_address
y write_data
realmente no importan cuando la write_enable
señ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_1164
paquete define el valor'-' -- Don't care
parastd_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 lascase?
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_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 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;
fuente
write_address
ywrite_data
? ¿Qué optimización esperas que tenga lugar?Respuestas:
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:
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 constd_match()
, lo que consideraría usar para su decodificación, por ejemplo:Aunque en ese punto, probablemente sea mejor que solo lo uses
case?
.fuente
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 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.fuente
if signal = '1' then a; else b; end if;
. Lamentablemente elsignal
no era1
o0
pero-
. Entonces, en la simulación, laelse
rama se ejecutó, pero en hardware-
resultó ser una1
, así que la rama verdadera se ejecutó ...'U'
s, comunes al comienzo de las simulaciones, que se han utilizado para queelse
se 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.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;
yif write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;
. Más lo mismo parawrite_data
. Juntos, eso debería detectar casi todos estos errores.