Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial Internet of Things >> Integrado

Descrevendo circuitos combinacionais em Verilog

06 de janeiro de 2019 pelo Dr. Steve Arar

Este artigo apresenta as técnicas para descrever circuitos combinacionais em Verilog, examinando como usar o operador condicional para descrever tabelas de verdade combinacionais.


Este artigo apresenta as técnicas para descrever circuitos combinacionais em Verilog, examinando como usar o operador condicional para descrever tabelas de verdade combinatórias. Ele também mostra como utilizar o bloco “sempre” da Verilog para descrever circuitos combinacionais - um bloco “sempre” pode nos fornecer uma solução ainda mais fácil para descrever um circuito digital.

Em um artigo anterior, discutimos o uso da palavra-chave “atribuir” da Verilog para realizar uma atribuição contínua. Essas atribuições estão sempre ativas e podem ser usadas para adquirir uma descrição de nível de porta de circuitos digitais. Por exemplo, no código a seguir, que descreve uma porta AND, o lado direito é avaliado continuamente e o resultado é colocado na rede out1:
  atribuir out1 =a &b;  

Verilog tem um operador condicional (? :) que nos permite verificar uma condição antes de fazer tais atribuições. A sintaxe é fornecida a seguir:
  atribuir [nome_do_sinal] =[expressão_condicional]? [valor_se_verdadeiro]:[valor_se_falso];  

A “expressão_condicional” é avaliada. Se for verdade, “valor_se_verdadeiro” é atribuído a “nome_do_sinal”. Se não for verdade, “nome_do_sinal” obtém “valor_se_falso”. Como exemplo, considere o seguinte código:
  atribuir out1 =(sel)? (a &b):(a | b);  

Se “sel” for verdadeiro, a &b serão atribuídos a “out1”. Se não for verdade, “out1” receberá a | b. Portanto, o código acima implementa a funcionalidade de um multiplexador 2 para 1. A implementação conceitual desse código pode ser conforme mostrado na Figura 1 abaixo.

A atribuição condicional nos permite ter uma descrição mais abstrata de certos circuitos porque tem a funcionalidade de uma instrução “if” encontrada nas linguagens de programação de computador tradicionais. O operador condicional pode ser usado em uma forma aninhada para implementar circuitos mais complexos. O exemplo 1 discute esses detalhes.

Exemplo 1:Operadores condicionais aninhados


Use o operador condicional (? :) para descrever um codificador de prioridade 4 para 2 com a seguinte tabela verdade:







O código Verilog para este codificador de prioridade é fornecido abaixo:
módulo
  Prio_4_to_2 (fio de entrada [3:0] x, fio de saída [1:0] y, fio de saída v); atribuir y =x [3]? 2'b11:x [2]? 2'b10:x [1]? 2'b01:2'b00; atribuir v =(x [3] | x [2] | x [1] | x [0])? 1'b1:1'b0; endmodule  

Além das linhas 7 a 10, o código contém os elementos básicos da linguagem discutidos em nosso artigo anterior. Então, vamos dar uma olhada nessas linhas.

Os termos 2’b11, 2’b10, 2’b01 referem-se às notações Verilog que representam números binários de dois bits. Em geral, o primeiro número (antes de ‘b) especifica o número de bits. A letra b especifica que os números são binários. Os dígitos após 'b fornecem o valor do número. Portanto, 2’b01 é a notação Verilog para representar um número binário de dois bits com valor 01 e 3’b100 denota um número binário de três bits com valor 100.

A linha 7 verifica o MSB da entrada, x [3], em um operador condicional. Se x [3] =1, a condição é avaliada como verdadeira e 2’b11 é atribuído a y (o valor atribuído é obtido da tabela verdade). Se x [3] =0, a condição é avaliada como falsa e a expressão após os dois pontos (:) será atribuída a y. A expressão após os dois pontos é o código da Linha 8, que é outro operador condicional.

O segundo operador condicional na linha 8 examina o segundo bit mais significativo da entrada, x [2], para determinar se 2'b10 deve ser atribuído a y ou a expressão após os dois pontos que é novamente outro operador condicional (linha 9) ser avaliados. Você pode verificar se os valores atribuídos a y correspondem à tabela verdade fornecida.

A saída válida (v) da tabela verdade será lógica alta se pelo menos um bit da entrada for lógica alta. A linha 11 mostra essa descrição aplicando o operador OR bit a bit (|) aos bits da entrada. Uma simulação Xilinx ISE do código acima é mostrada na Figura 2.




Figura 2. Uma simulação Xilinx ISE do código acima.

É importante notar que as expressões condicionais são avaliadas sucessivamente até que uma expressão verdadeira seja encontrada. A atribuição correspondente a esta expressão verdadeira será realizada. Como resultado, as expressões avaliadas anteriormente têm uma prioridade mais alta em comparação com as próximas. Isso significa que, teoricamente, um operador condicional é mais adequado para implementar uma rede prioritária (Figura 3) do que uma estrutura balanceada como um multiplexador (Figura 4).




Figura 3. Uma rede prioritária.




Figura 4. Um multiplexador n-para-1 em que não há prioridade entre as entradas.



Um artigo anterior revela uma discussão semelhante sobre atribuições simultâneas de VHDL.

Declarações de procedimentos Verilog


Podemos separar qualquer circuito combinacional em algumas portas lógicas básicas (AND, OR, NOT, etc.) e usar a instrução “atribuir” para descrever essas portas (uma descrição no nível da porta). Também podemos usar o operador condicional discutido na seção anterior para ter uma maneira mais abstrata de descrever alguns circuitos combinacionais (semelhante à instrução “if” das linguagens de programação de computador). No entanto, ainda há uma solução mais poderosa:usar o bloco "sempre" da Verilog.

Dentro de um bloco “sempre”, podemos ter instruções procedimentais que são executadas em sequência. Além disso, o bloco “sempre” suporta construções de linguagem abstratas, como declarações “if” e “case”.

O recurso de execução sequencial junto com as construções de linguagem abstrata disponíveis em um bloco “sempre” nos permite descrever mais facilmente a funcionalidade de um circuito, devido ao fato de que o raciocínio humano tem uma natureza sequencial e depende de descrições abstratas. Normalmente pensamos de forma algorítmica de alto nível, em vez de em termos de portas lógicas de baixo nível. Um bloco “sempre” pode nos fornecer uma solução mais fácil para descrever um circuito digital. Para obter mais detalhes sobre por que HDLs suportam descrições baseadas em declarações sequenciais, consulte meu artigo Introdução às declarações VHDL sequenciais.

Exemplo 2:Declarações em bloco "Sempre"


A sintaxe simplificada de um bloco “sempre” é fornecida a seguir:
  sempre @ (sensibilidade_lista) começar sequential_statements; fim  

A sensibilidade_list especifica quando as instruções sequenciais dentro do bloco “sempre” devem ser executadas. Por exemplo, considere usar o bloco “sempre” para descrever o circuito na Figura 5.




Figura 5. Circuit_1



Quando a ou b muda, a saída pode mudar, o que significa que a e b devem estar na lista de sensibilidade do bloco “sempre”. Em geral, para um circuito combinacional, todos os sinais de entrada devem ser incluídos na lista de sensibilidade.

Agora, podemos usar o operador AND bit a bit para descrever a funcionalidade do circuito (a &b) e atribuir o resultado à saída. Dentro de um bloco “sempre”, existem dois tipos diferentes de atribuições:a atribuição de bloqueio (=) e a atribuição sem bloqueio (<=). Usando a atribuição de bloqueio, obtemos o seguinte código:
  always @ (a, b) begin out1 =a &b; end  

Qual é a diferença entre uma atribuição de bloqueio e uma atribuição sem bloqueio?

Com uma atribuição de bloqueio, o lado direito é avaliado e imediatamente atribuído a out1. Portanto, quando a Linha 3 é executada, out1 é atualizado imediatamente antes de irmos para a próxima linha do código. O nome “atribuição de bloqueio” enfatiza que as próximas linhas são bloqueadas até que o lado esquerdo seja atualizado.

Com uma atribuição sem bloqueio, a expressão do lado direito é avaliada, mas não é aplicada à variável do lado esquerdo até chegarmos ao final do bloco "sempre". A escolha de atribuição bloqueadora ou não bloqueadora pode ser confusa para um iniciante e o uso impróprio delas pode levar a uma funcionalidade indesejada. Por exemplo, usar atribuições de bloqueio para inferir flip-flops pode introduzir uma condição de corrida.

Para este artigo introdutório, não entraremos em detalhes e nos limitaremos a uma orientação simples para evitar possíveis armadilhas:Use as atribuições de bloqueio ao escrever o código para um circuito combinacional. Portanto, o bloco “sempre” na Listagem 1 será usado para descrever uma porta AND.

Em um artigo anterior, nos familiarizamos com o tipo de dados “wire” da Verilog. Este tipo de dados representa um fio físico em nosso projeto FPGA. Dentro de um bloco "sempre", o padrão Verilog não nos permite atribuir um valor a um "fio". Em vez disso, usamos o tipo de dados “reg”. O nome “reg” é um pouco confuso, mas observe que um “reg” pode ou não levar a um elemento de armazenamento físico em seu design. O código a seguir é a descrição Verilog da Figura 5 usando um bloco “sempre”. Observe que o tipo de dados de saída deve ser “reg” porque obtém seu valor de uma atribuição procedural.
Módulo
  Circuit_1 (fio de entrada a, fio de entrada b, saída reg saída1); sempre @ (a, b) começa1 =a &b; end endmodule  

Neste artigo, nos familiarizamos com o operador condicional Verilog. Usamos a forma aninhada desse operador para descrever um codificador de prioridade. Em seguida, tocamos em uma construção de linguagem mais poderosa, o bloco “sempre”, para descrever os circuitos combinacionais. Em artigos futuros, examinaremos o uso do bloco "sempre" para implementar circuitos sequenciais.

Integrado

  1. Introdução ao Verilog
  2. Introdução aos Circuitos DC
  3. Introdução aos circuitos CA
  4. Usando Múltiplos Circuitos Combinacionais
  5. Circuitos de controle
  6. Operadores C#
  7. C# ternário (? :) Operador
  8. Tutorial Verilog
  9. Concatenação Verilog
  10. Compilação condicional Verilog `ifdef