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.