Como posso especificar sinais "não me importo" em VHDL?

11

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_addresse write_datarealmente não importam quando o write_enablesinal é '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 recordtipo, pois as constantes de registro precisam ser totalmente especificadas (pelo menos o Modelsim me diz isso).
  • O std_logic_1164pacote define o valor '-' -- Don't carepara std_ulogic. Parece que é a escolha semanticamente correta para um "não se importe" explícito, mas nunca o vi usado em nenhum lugar (exceto nas case?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_addresspode 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;
Fritz
fonte
Você poderia explicar o que está tentando fazer write_addresse write_data? Qual otimização você espera que ocorra?
Fru1tbat
Espero que o exemplo deixe mais claro o que quero alcançar.
Fritz

Respostas:

9

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:

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;

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 com std_match(), o que eu consideraria usar para sua decodificação, por exemplo:

if std_match(instruction(15 downto 8), "1100----") then

Embora nesse ponto, você provavelmente esteja melhor apenas usando case?.

fru1tbat
fonte
6

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 (ref .: "Guia do Usuário XST")
  • Altera (ref .: "Estilos de codificação HDL recomendados")
  • Synplify (ref .: "Synplify reference manual")

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.

Carl
fonte
1
Eu tive outra aplicação dessa desvantagem. O código trabalhou na simulação, mas não no FPGA, porque meu código parecia: if signal = '1' then a; else b; end if;. Infelizmente o signalnão era 1ou 0mas -. Assim, na simulação, o elseramo foi executado, mas no hardware -acabou sendo um 1, então o ramo verdadeiro foi executado ...
Fritz
Sim, eu tive bugs similares na simulação, mas na maioria das vezes existem 'U's, comuns no início das simulações, que foram usados, levando à elseexecuçã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.
Carl
Depois de encontrar o bug, escrevi algo como sempreif 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;e if write_enable = '1' then assert not is_X(write_addr) report "write_addr=str(write_addr) severity ERROR; end if;. Mais o mesmo para write_data. Juntos, isso deve pegar quase todos esses erros.
Fritz
Essa é uma maneira, mas é muito detalhada para mim. Eu gostaria dessa possibilidade dentro da linguagem VHDL.
Carl
1
Bem, sim, o VHDL é um pouco detalhado, mas essa é apenas a maneira do VHDL. : D Por outro lado, também é muito explícito e não faz "magia negra" nas minhas costas, o que eu acho bastante agradável (cf. O Zen do Python "Explícito é melhor que implícito").
Fritz