Meu compilador reclama de travas inferidas em meus loops combinatórios ( always @(*)
, no Verilog). Também me disseram que travas inferidas devem ser preferencialmente evitadas.
O que exatamente há de errado com travas inferidas? Eles certamente tornam os loops combinatórios mais fáceis de escrever.
Respostas:
Uma "trava" é diferente de um "flip-flop", pois um FF muda apenas sua saída em resposta a uma borda do relógio. Uma trava pode alterar sua saída em resposta a algo diferente de um relógio. Por exemplo, um SR-Latch possui um conjunto e uma entrada de redefinição e, se um deles estiver ativo, a saída poderá mudar. Onde, como SR-FF, responde apenas a um conjunto ou redefinição quando há também uma borda do relógio.
Em um FPGA, você deseja que sua lógica seja totalmente síncrona. Isso significa que todos os elementos de armazenamento (como os FFs) têm clock de uma única fonte de relógio. Qualquer coisa assíncrona a esse relógio precisa ser tratada com muito cuidado, caso contrário, ocorrerão erros de temporização.
Uma trava é basicamente um elemento de armazenamento assíncrono. Não possui entrada de relógio e, portanto, não pode ser sincronizado com nenhum relógio. Devo observar que existem FFs com redefinições assíncronas e entradas de redefinição, e elas devem ser tratadas com o mesmo cuidado que as travas normais.
Entrar em todos os problemas de tempo que as travas podem causar está muito além do que pode ser abordado aqui, mas deixe-me dar um exemplo:
Digamos que você tenha um SR-Latch e deseje que ele seja definido sempre que um contador de 8 bits atingir um determinado valor. Não sei ao certo qual seria o código Verilog, mas em VHDL o código é: set <= '1' when count = "11010010" else '0'; Esse sinal definido vai para a entrada definida em nosso SR-Latch.
A lógica gerada é puramente combinatória; uma mistura de and-gates, or-gates e inversores (ou LUTs). Mas os caminhos do sinal através dessa lógica combinatória nem sempre são perfeitos e o sinal "definido" pode ter falhas nele. O caminho do sinal através de um grupo específico de portas pode levar mais tempo que outro grupo, fazendo com que a saída definida fique ativa por um breve momento antes que a saída se estabeleça no estado final.
Essa falha de saída pode fazer com que nosso SR-Latch seja configurado, mesmo que não devesse. Se mudarmos de um SR-Latch para um SR-FF, com o mesmo relógio que o contador, o SR-FF aguardará um ciclo de relógio inteiro antes de mudar de estado. Em essência, ele aguardará o sinal definido antes de olhar para ele.
Se os caminhos através da lógica combinatória para o sinal definido forem roteados de maneira diferente (causando atrasos diferentes), o comportamento da falha também mudará. A lógica pode funcionar bem, mas, como você alterou algo totalmente não relacionado, essa lógica é roteada de maneira diferente e, portanto, o bug é exibido. A temperatura e a tensão também alteram o tempo do sinal e, portanto, podem alterar o comportamento da falha.
Essa incerteza no momento é o motivo pelo qual você deve evitar trincos na sua lógica. Os FFs são muito mais seguros de usar. É por isso que o seu compilador está avisando sobre travas, pois é fácil fazer uma trava por engano e você provavelmente não a quer lá de qualquer maneira.
Obviamente, às vezes são necessárias travas. Você só precisa usá-los muito raramente, somente quando absolutamente necessário, e então deve projetar a lógica corretamente, para que não haja falhas possíveis.
fonte
O que faz uma trava inferida?
Para a lógica combinatória, a saída do circuito é apenas uma função de entrada e não deve conter nenhuma memória ou estado interno (trava).
No Verilog, uma variável manterá seu valor anterior se não lhe for atribuído um valor em um bloco always . Uma trava deve ser criada para armazenar esse valor presente.
Uma instrução if-else incompleta gerará travas. Uma instrução if-else é considerada "incompleta" se o estado de saída não estiver definido para todas as possíveis condições de entrada. O mesmo vale para uma declaração de caso incompleta ou para uma declaração de caso que não possui um item padrão:
Por que as travas inferidas são ruins?
Travas inferidas podem servir como um 'sinal de aviso' de que o design lógico pode não ser implementado conforme planejado. Umainstrução if-else ou case crucialpode estar faltando no design.
Travas podem levar a problemas de tempo e condições de corrida. Eles podem levar ao feedback combinatório - roteamento da saída de volta à entrada - o que pode ser imprevisível.
Para evitar a criação de travas inferidas:
Algumas partes parafraseadas em "Prototipagem FPGA da Verilog Examples" de P. Chu
fonte
As travas são muito difíceis de usar em FPGAs ou CPLDs, muitas pessoas simplesmente as evitam completamente. Uma das razões é que muitos FPGAs não possuem uma trava embutida, portanto são feitos de portas lógicas - isso pode causar problemas desagradáveis de tempo.
Além disso, você não tem controle sobre atrasos de tempo e condições de corrida ao usar uma trava (a menos que haja um elemento nativo)
Eu desaconselharia o uso de travas, a menos que você não possa prescindir delas (por exemplo, emprestar tempo para atender a uma freqüência de clock máxima necessária) e usar técnicas de codificação para tornar menos provável a ocorrência de travas inferenciais acidentalmente.
fonte
Projetos lógicos seqüenciais construídos usando lógica combinatória e feedback geralmente assumem uma suposição razoável quando se usa portões físicos: que a saída de um portão não será alterada em resposta a uma mudança na entrada, até algum tempo após a entrada ter realmente mudado. Há algumas ocasiões em que essa suposição pode não se manter ao usar portas reais (por exemplo, se um portão NOR rápido e um inversor rápido forem ambos acionados por um sinal que sobe lentamente de VSS para VDD, e se o inversor alternar a 1,2 volts enquanto o NOR o gate não muda até 1,7 volts, o gate NOR pode ver a saída do inversor baixa antes de ver que o sinal de aumento lento subiu), mas esses problemas geralmente podem ser resolvidos adicionando-se um buffer sempre que uma mudança lenta o sinal é roteado para mais de um destino. Infelizmente,
O problema é que, a menos que seja explicitamente instruído de outra forma, um compilador FPGA pode substituir arbitrariamente um circuito combinatório por um circuito totalmente diferente, com o mesmo comportamento em estado estacionário, mas com um tempo totalmente diferente. Por exemplo, suponha que uma função combinatória complexa F receba seis entradas U a Z. F seja alimentada diretamente no circuito P e (F NAND Z) seja alimentada no circuito Q. O compilador pode perceber que o valor alimentado para Q dependerá apenas de F quando Z é alto e pode calcular uma função F 'que é como F, exceto que Z é considerado alto; Q pode então ser alimentado com (F 'NAND Z) em vez de (F NAND Z). Seria perfeitamente possível que a realização mais eficiente de P tivesse cinco atrasos de porta, mas a realização mais eficiente de Q teria apenas dois. Portanto,
Se um circuito tiver um loop de feedback combinatório, um compilador FPGA precisará adicionar nós de sinal físico que fisicamente terão um atraso positivo (um loop de feedback de atraso zero não pode existir no mundo real), mas não há garantia de que esses nós seriam adicionados nos locais necessários para fazer o circuito se comportar conforme desejado. Também não há garantia de que uma leve alteração no design não faça com que o compilador mude de um posicionamento arbitrário que funciona no mundo real, para um posicionamento arbitrário diferente que falha.
fonte
Detalhes sobre como capturar uma trava no design são explicados brevemente neste link.
https://www.doulos.com/knowhow/fpga/latches/
fonte