À primeira vista, você esperaria que o código fonte VHDL abaixo se comportasse como um registro de turno. Nesse q, com o tempo seria
"UUUU0", "UUU00", "UU000", "U0000", "00000", ....
mas, em vez disso, é sempre U
depois de cinco (ou mais) ciclos de relógio consecutivos.
Por que é isso?
Este código é na verdade uma versão muito simplificada de uma simulação muito mais complicada. Mas demonstra os sintomas que vejo.
Ele exibe esse resultado interessante e inesperado durante a simulação, tanto no ModelSim quanto no ActiveHDL. Não tentei outros simuladores e gostaria (em segundo lugar de uma explicação da causa) de saber se outros agem da mesma maneira.
Para responder a essa pergunta corretamente, você deve entender que:
- Sei que essa não é a melhor maneira de implementar um registro de turno
- Eu sei que para síntese RTL isso deve ter uma redefinição.
- Eu sei que uma matriz de std_logic é um std_logic_vector.
- Eu sei do operador de agregação
&
,.
O que eu também encontrei:
- Se a atribuição
temp(0)<='0';
for movida dentro do processo, ela funcionará. - Se o loop estiver desembrulhado (consulte o código comentado), ele funcionará.
Reiterarei que esta é uma versão muito simplificada de um design muito mais complicado (para uma CPU em pipeline), configurada para mostrar puramente os resultados inesperados da simulação. Os tipos de sinal reais são apenas uma simplificação. Por esse motivo, você deve considerar suas respostas com o código no formulário como está.
Meu palpite é que o otimizador do mecanismo de simulação VHDL, por engano (ou talvez de acordo com a especificação), não se incomoda em executar as expressões dentro do loop, pois nenhum sinal está fora da mudança, embora eu possa refutar isso colocando o loop não empacotado em um loop.
Portanto, espero que a resposta a esta pergunta tenha mais a ver com os padrões para simulação VHDL da sintaxe inexplicável VHDL e como os mecanismos de simulação VHDL fazem suas otimizações, em vez de se um exemplo de código é a melhor maneira de fazer algo ou não.
E agora para o código que estou simulando:
library ieee;
use ieee.std_logic_1164.all;
entity test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end entity;
architecture example of test_simple is
type t_temp is array(4 downto 0) of std_logic;
signal temp : t_temp;
begin
temp(0) <= '0';
p : process (clk)
begin
if rising_edge(clk) then
for i in 1 to 4 loop
temp(i) <= temp(i - 1);
end loop;
--temp(1) <= temp(0);
--temp(2) <= temp(1);
--temp(3) <= temp(2);
--temp(4) <= temp(3);
end if;
end process p;
q <= temp(4);
end architecture;
E o banco de ensaio:
library ieee;
use ieee.std_logic_1164.all;
entity Bench is
end entity;
architecture tb of bench is
component test_simple is
port (
clk : in std_logic;
q : out std_logic
);
end component;
signal clk:std_logic:='0';
signal q:std_logic;
signal rst:std_logic;
constant freq:real:=100.0e3;
begin
clk<=not clk after 0.5 sec / freq;
TB:process
begin
rst<='1';
wait for 10 us;
rst<='0';
wait for 100 us;
wait;
end process;
--Note: rst is not connected
UUT:test_simple port map (clk=>clk,q=>q) ;
end architecture;
fonte
temp(0)
porque não há "eventos" associados à constante literal. Colocar a atribuição dentro deprocess
cria uma associação com os eventos do relógio que fazem com que ela funcione. Gostaria de saber se adicionar umaafter
cláusula à atribuição seria uma solução potencial.Respostas:
Tem a ver com o que pode ser facilmente avaliado no momento da elaboração, formalmente, o que é chamado de "expressão estática localmente". Essa é uma regra de aparência obscura, mas merece alguma reflexão - eventualmente faz algum sentido, e seu simulador está certo em alertá-lo, gerando resultados não óbvios.
Agora,
temp(1)
pode ser avaliado em tempo de compilação (ainda mais cedo que o tempo de elaboração) e pode gerar um driver no bit 1 de "temp".No entanto,
temp(i)
envolve um pouco mais de trabalho para as ferramentas. Dada a natureza trivial dos limites de loop aqui (1 a 4), é óbvio para nós humanos que a temperatura (0) não pode ser acionada e o que você está fazendo é seguro. Mas imagine que os limites eram funçõeslower(foo) to upper(bar)
em um pacote declarado em outro lugar ... agora o máximo que você pode dizer com certeza é quetemp
é acionado - então a expressão "localmente estática" étemp
.E isso significa que o processo é restrito por essas regras a todas
temp
, e nesse ponto você tem vários driverstemp(0)
- a condução do processo (sem valor inicial, ou seja, 'u') e a externatemp(0) <= '0';
. Então, naturalmente, os dois drivers decidem 'U'.A alternativa seria uma "pequena regra hacky" (opinião) de que se os limites do loop fossem constantes, faça uma coisa, mas se eles foram declarados como outra coisa, faça outra coisa e assim por diante ... as regras mais estranhas quanto mais complexa a linguagem se torna ... na minha opinião, não é uma solução melhor.
fonte