Nos cursos de Design Lógico, todos aprendemos que é possível minimizar uma função lógica, por exemplo, usando um mapa de Karnaugh ou o algoritmo Quine – McCluskey . Também aprendemos que os valores "Não me importo" aumentam o potencial de minimização.
Por exemplo, pegue um arquivo de registro. Os sinais write_address
e write_data
realmente não importam quando o write_enable
sinal é '0'
. Portanto, eles devem receber um valor "Não se importam" para permitir mais otimizações na lógica que está direcionando esses sinais (ou seja, não no próprio arquivo de registro).
Qual é a maneira correta de especificar esses valores "Não me importo" em VHDL para permitir que a ferramenta de síntese tenha mais espaço para possíveis otimizações?
Até agora, encontrei as seguintes coisas que podem ser adequadas. Mas não tenho muita certeza de quais são os prós e os contras de cada abordagem:
- Simplesmente não atribuindo o sinal. Parece que poderia funcionar. No entanto, descobri que não funciona quando você deseja definir um tipo "não faça constante" de algum
record
tipo, pois as constantes de registro precisam ser totalmente especificadas (pelo menos o Modelsim me diz isso). - O
std_logic_1164
pacote define o valor'-' -- Don't care
parastd_ulogic
. Parece que é a escolha semanticamente correta para um "não se importe" explícito, mas nunca o vi usado em nenhum lugar (exceto nascase?
construções não relacionadas do VHDL-2008 ). - Modelsim usa o valor
'X'
para exibir sinais indefinidos. No entanto, não tenho certeza se as ferramentas de síntese entendem uma'X'
atribuição explícita como "não me importo".
Aqui está um trecho de código simplificado demais para esclarecimentos, onde eu inicializei os sinais de não se importar '-'
.
Como você pode ver, o sinal control.reg_write_address
pode ter 3 valores diferentes: "----"
, instruction(11 downto 8);
e instruction(3 downto 0);
. Agora, eu espero que isso seja sintetizado em um multiplexador de 2 entradas se '-'
for interpretado como "não me importo". Se eu tivesse inicializado o sinal com, em (others => '0')
vez de '-'
, a ferramenta teria que gerar um multiplexador de 3 entradas.
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;
fonte
write_address
ewrite_data
? Qual otimização você espera que ocorra?Respostas:
Vou deixar que um especialista em LRM forneça uma resposta mais detalhada, mas, em suma, sua abordagem deve ser válida - executei um teste rápido com uma versão recente do Quartus e lida
'-'
com o que deveria - a lógica gerada é reduzida como esperado quando a saída é padronizada como'-'
('X'
funciona também, a propósito). Mais sobre as abordagens listadas:Não atribuir o sinal não é realmente uma opção para o seu exemplo, é claro, se você não deseja travas. Se for um processo cronometrado, você estará um pouco melhor, mas ainda terá habilitações onde talvez não precise delas. Talvez eu esteja perdendo sua intenção aqui.
'-'
, como observado anteriormente, é provavelmente a melhor opção, por razões semânticas e práticas.Depende do que você quer dizer com "indefinido".
'X'
é tecnicamente "desconhecido".'U'
é para sinais não inicializados, que o ModelSim exibe como"X"
para representações hexadecimais.'X'
parece funcionar, como observei acima.Outra alternativa seria fazer a otimização você mesmo e impedir que um caso fosse testado explicitamente:
No entanto, isso tem desvantagens significativas (principalmente relacionadas à clareza de código) e eu provavelmente optaria por uma solução mais ideal.
Aliás,
'-'
também é comumente usado comstd_match()
, o que eu consideraria usar para sua decodificação, por exemplo:Embora nesse ponto, você provavelmente esteja melhor apenas usando
case?
.fonte
Em resumo: é VHDL legal e normalmente é suportado por ferramentas de síntese.
No entanto, é bastante incomum vê-lo usado. Eu realmente não sei o porquê. Seu código parece-me um bom exemplo de quando seria significativo usá-lo.
Há, no entanto, uma desvantagem da qual devemos estar cientes: na síntese, as funções que conduzem as saídas onde não há cuidados estão envolvidas podem ser diferentes entre as execuções da síntese. Isso torna a síntese menos determinística. Se as saídas definidas como não importadas forem usadas (por engano), isso poderá dificultar a localização do erro.
Suporte de ferramenta
Pelo menos as seguintes ferramentas aceitarão não se importam e farão uso das possibilidades de otimização:
Xilinx e Altera tratam
'-'
e,'X'
como não se importam, o Synplify os trata e, além disso,'U'
e'W'
(fraco), como não se importam.fonte
if signal = '1' then a; else b; end if;
. Infelizmente osignal
não era1
ou0
mas-
. Assim, na simulação, oelse
ramo foi executado, mas no hardware-
acabou sendo um1
, então o ramo verdadeiro foi executado ...'U'
s, comuns no início das simulações, que foram usados, levando àelse
execução de algum bloco de código. Seria maravilhoso se, de alguma forma, condicionais pudessem ser feitos para propagar'U'
s, semelhante ao comportamento de expressões booleanas simultâneas.if signal = '1' then output <= '1'; elsif signal='0' then output <= '0'; else output <= '-'; end if;
. E adicionei o seguinte a todos os registros e memórias:assert not is_X(write_enable) report "we=" & str(A_write_enable) severity ERROR;
eif write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;
. Mais o mesmo parawrite_data
. Juntos, isso deve pegar quase todos esses erros.