Por que esse padrão VHDL simples para um registro de turnos não funciona conforme o esperado

8

À 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 Udepois 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;
Jason Morgan
fonte
primeira tentativa de inicializar temporário na declaração de sinal, eu descobri simuladores VHDL são peculiares sobre onde você inicializa coisas
Matt
Parece que o simulador está ignorando a atribuição simultânea temp(0)porque não há "eventos" associados à constante literal. Colocar a atribuição dentro de processcria uma associação com os eventos do relógio que fazem com que ela funcione. Gostaria de saber se adicionar uma aftercláusula à atribuição seria uma solução potencial.
21412 Dave McNeedy:

Respostas:

7

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ções lower(foo) to upper(bar)em um pacote declarado em outro lugar ... agora o máximo que você pode dizer com certeza é que tempé 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 drivers temp(0)- a condução do processo (sem valor inicial, ou seja, 'u') e a externa temp(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.

Brian Drummond
fonte
Boa resposta (+1), mas eu discordo da sua caracterização de "pequena regra hacky". O ponto principal da simulação é representar o comportamento do hardware real. Eu entendo as restrições criadas pela compilação independente de módulos individuais, mas acho que a regra deve ser que qualquer coisa que pode ser avaliada em tempo de compilação deve ser. Essa seria uma regra muito mais geral e ajudaria o sistema a aderir ao princípio da "menor surpresa". Permitir que as ferramentas não executem essas avaliações parece mais "hacky" para mim.
Dave Tweed
Comentário justo - Ada, por exemplo, tem (e expressa formalmente) muito mais complexidade sobre regras como essa, e consegue apresentar uma visão muito mais simples para os usuários (sem o fator WTF de C!). O VHDL foi originalmente simplificado (IMO um pouco longe demais) do Ada. Mas talvez ele poderia adotar "Tipo de congelamento" de Ada regras que permitam esse tipo de otimização quando claramente seguro (como aqui) e proibir o contrário ...
Brian Drummond
Obrigado Brian, o que você diz certamente faz sentido. A idéia de uma regra simples e não de muitas regras obscuras também parece fazer sentido. Você diria que esse comportamento é verdadeiro (e de fato especificado) para todos os simuladores ou são apenas os dois que eu tentei?
11132 Jason Morgan
2
Se eu encontrasse um que fizesse algo diferente, eu arquivaria um bug contra ele! Uma coisa que os maiores detratores do VHDL dirão a seu favor é que ele garante resultados consistentes de simulação nos casos em que outros idiomas (não apenas o Verilog) não. (embora sim, às vezes suas deficiências bug me também!)
Brian Drummond
1
Experiência de correção rápida: se minha resposta estiver correta, você pode dirigir "temp (0) <= 'Z';" dentro do processo, portanto, "desconectando" o driver fantasma, e o driver externo funcionará ...
Brian Drummond