O uso de sincronizadores 2-ff é um padrão para que um sinal ultrapasse os limites do relógio. E há muitos papéis / figuras ilustrando o mecanismo, como este:
Parece que o bclk pode apenas amostrar o pulso do adat uma vez (na segunda borda ascendente do bclk ), o que causa a metaestabilidade de saída no bq1_dat . Como o bq1_dat pode ser amostrado "alto" no próximo limite do relógio ativo?
Além da minha pergunta, gostaria de adicionar o que acho que um sinal passa com segurança para outro domínio do relógio (suponha que 2-FF seja suficiente para atender aos requisitos de MTBF). Por favor, corrija-me se houver algum erro.
ps: O estado metaestável não exibe a forma de onda "vagando", mas um nível que não é '1' nem '0'. A figura a seguir mostra um exemplo de saída metaestável.
A figura original veio das anotações da EE108A, Aula 13: Falha na metaestabilidade e sincronização (ow When Good Flip-Flops Bad) de WJ Dally.
fonte
Respostas:
A resposta simples é que eles não fazem por conta própria. O sincronizador não existe para garantir que os dados sejam transmitidos, mas para garantir que você não termine com sinais metaestáveis alimentando muitos outros sinais e causando problemas. O segundo FF, como mostra o diagrama, captura a primeira saída FF metaestável e evita que ela se propague mais através do design.
Existem vários tipos de sinais, e como você inclui sincronizadores depende de qual sinal você está falando. Mas vamos ver alguns tipos comuns:
Sinais de acionamento - ou qualquer sinal que seja basicamente um pulso que deve iniciar outra coisa em execução. Eles geralmente não carregam dados, e tudo o que você está interessado é que haja, digamos, uma vantagem crescente para iniciar algo acontecendo em outro domínio do relógio. Para fazer o cruzamento, você precisaria de um sincronizador (essencialmente fazendo o que é mostrado em seu diagrama), mas precisará de um pouco mais.
A opção mais simples é estender o pulso - essencialmente, você deve garantir que o pulso de entrada tenha mais de 1 período de relógio do relógio de destino (deve ser maior que 1 ciclo, pelo menos, o maior tempo de configuração e espera do registro de destino) . Por exemplo, se você estiver passando de um relógio de 20 MHz para um relógio de 15 MHz, verifique se o pulso tem dois ciclos de relógio na entrada, o que garantiria que ele seja apresentado ao relógio de destino e não seja perdido. Isso também responde à sua pergunta sobre como o sinal é garantido. Se o pulso for maior que um período do relógio de destino, significa que, se for metaestável no primeiro limite do relógio e acabar sendo visto como 0, então no segundo limite do relógio, ele definitivamente capturará o pulso.
Como com esse tipo de sinal, você só está interessado em que o pulso tenha passado, não importa se o sinal de saída termina com dois ciclos de clock altos algumas vezes e apenas um ciclo do resto. Se você precisar garantir que seja um pulso de ciclo único, instale um circuito simples de detecção de borda.
Barramentos de controle - ou possivelmente tipos de barramentos de dados. Estes são sem dúvida mais difíceis, porque se você tiver um fluxo de dados de vários bits, ele precisará permanecer sincronizado. Nesse caso, o que você faria é implementar algo chamado "handshaking". Você basicamente carrega seus dados no relógio de origem e os segura. Em seguida, você envia um sinal de solicitação (como em 1) através de um sincronizador. Depois que o sinal de solicitação é transmitido, você sabe que o barramento de dados também será estabilizado no domínio de destino. Você pode cronometrar em um banco de registro no destino. O destino envia novamente um pulso de confirmação para informar a fonte de que pode carregar a próxima palavra.
Você usaria esse tipo de barramento se precisasse enviar uma palavra de controle para o relógio de destino, para o qual precisa saber que ela chegou antes de enviar outra (por exemplo, se você estiver enviando um comando para fazer alguma coisa).
Barramentos de dados - para dados em que você tem uma fonte que divulga dados continuamente ou em rajadas, é melhor usar um FIFO do que sincronizadores. O FIFO usa uma memória de relógio duplo para armazenar os dados, juntamente com contadores para acompanhar a quantidade de dados no FIFO. Você grava os dados no FIFO quando há espaço e depois incrementa o endereço de gravação. Esse endereço é tipicamente codificado em um esquema "Gray Coding", que garante que cada incremento no endereço cause apenas umbit no barramento de endereços para mudar (o que significa que você não precisa sincronizar vários bits). Esse endereço é então transferido para o domínio de destino (através de uma de suas cadeias sincronizadoras), onde é comparado com o endereço de leitura. Se houver dados no FIFO, eles poderão ser lidos na memória usando a porta do relógio de destino. O endereço de leitura também é codificado em cinza e enviado de volta à fonte através de outro sincronizador, para que a porta de gravação possa calcular se há algum espaço no FIFO.
Redefinir sinais - normalmente, eles usam uma versão modificada do sincronizador no que é conhecido como "Assíncrona assertiva, desativada síncrona". Nesta versão modificada, a entrada de dados para o primeiro flip-flop é vinculada ao GND e, em vez disso, o sinal de redefinição de entrada é conectado aos sinais predefinidos assíncronos de cada flip-flop no sincronizador. Isso resulta em um sinal de saída que é totalmente assíncrono quando aumenta, mas a cadeia do sincronizador garante que diminua de forma síncrona com o relógio de destino, marcando zeros na cadeia de registro.
Esse tipo de sincronizador é terrível para dados e controle, mas perfeitamente adequado para redefinir sinais. Se toda a lógica de destino alimentar a saída dessa cadeia nas entradas de redefinição assíncrona de qualquer registro no domínio, haverá pouca preocupação com a metastabilidade na declaração (mesmo que seja assíncrona), pois todos os registros são forçados a um estado conhecido. Então, quando o sinal de redefinição é desativado no domínio de origem, ele é desativado de forma síncrona no domínio de destino, o que significa que todos os registros saem da redefinição no mesmo ciclo de clock (em vez de +/- 1 ciclo, se foi desativado de forma assíncrona).
Como você pode ver acima, é muito mais complexo fazer uma travessia no domínio do relógio do que simplesmente colocar um sincronizador de 2 flip-flop no sinal. O método exato usado depende da aplicação.
fonte
sync_Bits
) para FPGAs Xilinx e Altera para melhorar o comportamento de metaestabilidade. O sincronizador 2-FF é usado, por exemplo,sync_Strobe
para criar sincronizadores mais complexos para pulsos.1) Usando seu desenho como exemplo, aclk e bclk são assíncronos entre si. Em outras palavras, eles têm diferentes fontes de relógio. Eles estão mostrando adat como dados válidos, mas sincronizados apenas com o aclk. É aqui que o sincronizador bclk entra em ação.
2) Este desenho supõe um cenário de pior caso, em que bq1_dat é uma saída confusa porque o bq1 FF capturou apenas parte do final dos dados, criando um estado metaestável no qual a saída geralmente é lixo. Aqui está o truque. O Bq2 tem o mesmo bclk que o bq1, mas são necessários dois ciclos de relógio de bclk para que os dados passem e apareçam em bq2_dat.
3) O primeiro bclk capturou parte dos dados, resultando em uma saída confusa, mas o segundo bclk é um ciclo de clock mais tarde, tempo suficiente para que dados ambíguos do bq1_dat se estabeleçam em um estado alto ou baixo. O pulso bagunçado do bq1_dat durou apenas o tempo suficiente para o bq2 capturar uma lógica válida '1' (lógica alta) e passá-la para bq2_dat como dados válidos e agora sincronizados (lógica alta).
4) A jusante, qualquer relógio usando bclk terá dados sincronizados para trabalhar. Observe que apenas o primeiro bclk FF teve que lidar com um estado metaestável . A saída poderia ter sido uma lógica baixa se o adat estivesse atrasado apenas pico ou nano segundos. Lembre-se de que esses chinelos mostram a entrada de dados apenas na borda ascendente do relógio. O que acontece antes ou depois da borda ascendente é ignorado.
fonte