ÍNDICE DE CONTEÚDO
- Tutorial de Verilog – Operadores
- Tutorial de Verilog – Ponto Fixo e Ponto Flutuante em Verilog – Parte I
- Tutorial de Verilog – O primeiro Projeto com Quartus
- Tutorial de Verilog – 7 formas de representar um MUX em Verilog
- Tutorial de Verilog: Decodificador ou DEMUX
- Tutorial de Verilog: Meio Somador (Half Adder)
- Tutorial de Verilog: Somador Completo (full adder)
- Tutorial de Verilog: Conversor de Código Binário para Código Gray
- Tutorial de Verilog: Conversor de Código Gray para Código Binário
- Tutorial de Verilog: Somador com Propagação do Carry – Somador Ripple-Carry
- Tutorial de Verilog: Conversor BCD para 7 Segmentos
- Tutorial de Verilog: Contador binário síncrono crescente Mod 10 com reset síncrono em nível lógico baixo
Dando prosseguimento ao Tutorial de Verilog, vou mostrar abaixo sete formas diferentes de implementar um mesmo circuito, o MUX, apresentado anteriormente.
A seguir vou implementar um MUX em Verilog de sete abordagens diferentes, testar o circuito e imprimir os resultados e o circuito gerado. Utilizo nessa abordagem o Quartus 13 da Altera, software grátis para desenvolvimento de circuitos simples e legal para trabalhar com HDL no Windows. Qualquer pessoa pode reproduzir essas experiências ou utilizar o código como bem entender.
1. MUX usando portas lógicas
Implemento a seguir o circuito MUX utilizando portas lógicas.
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX1( A, B, S, X); input wire A, B; // As entradas sao A e B input wire S; // O sinal de selecao é S output wire X; // O sinal de saida é X wire S0_inv, a1, b1; not u1( S0_inv, S ); and u2( a1, S0_inv, A ); and u3( b1, S, B ); or u4( X, a1, b1 ); endmodule |
Comentários sobre o código Verilog:
Ao utilizar portas lógicas, utilizei na frente do nome da porta lógica um nome para esse componente, como se estivesse criando um componente elétrico em uma ferramenta de esquemático. Utilizei os nomes u1, u2, u3 e u4 para isso. Esse rótulo é utilizado para se criar o Netlist, ou hardware resultante do projeto.
A estrutura de uma porta lógica em verilog sempre é:
portaLogica nome (saida, entrada1, entrada2, … , entradaN)
Poderia ao invés de utilizar input wire utilizar apenas wire. O mesmo poderia fazer para output wire, onde poderia omitir a palavra wire neste caso.
Testbench:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//Verilog Code for Multiplexer TESTBENCH - 2 inputs and 1 Select //by Thiago Lima module MUX1_TB; reg A_tb, B_tb, S_tb; wire X_tb; MUX1 dut( A_tb, B_tb, S_tb, X_tb); initial begin A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); end endmodule |
Resultado no terminal:
Onda de Saída:
Circuito gerado:
Baixe o projeto completo MUX1 para o Quartus 13 ou 14.
2. MUX usando assign e condição ?
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX2( A, B, S, X); input A, B, S; output wire X; assign X = (S) ? B : A; endmodule |
Comentários sobre o código Verilog:
Utilizei uma estrutura onde a condição é testada, e se for verdadeira, executa uma condição e se não, a outra. Nesse caso, X recebe o valor de B se S for 1 e para qualquer outro valor de S, X recebe 0.
Utilizei o mesmo Testbench, trocando MUX1 por MUX2. Os resultados foram exatamente os mesmos para a simulação.
Circuito gerado:
Baixe aqui o projeto MUX2 para o Quartus 13 ou 14.
3. MUX usando if
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX3( A, B, S, X); input A, B, S; output reg X; always @( A or B or S ) begin if (S == 1'b0) begin X = A; end else begin X = B; end end endmodule |
Comentários sobre o código Verilog:
O código acima foi feito usando o padrão comportamental (behavioral), que é um padrão de mais alto nível, para que o modelamento da lógica seja feito conforme o comportamento esperado pelo circuito projetado. Utilizei if else, como utilizado na linguagem C. Nesse bloco, analiso a condição do sinal de entrada S, utilizando if e else, e tomo ação dependendo do valor de S. Essa interação always sempre acontece quando um dos sinais dentro da lista entre parenteses é alterado. Neste caso considerei todos os sinais de entrada. Então poderia usar:
1 |
always @( A or B or S ) |
1 |
always @* |
Utilizei o mesmo Testbench, trocando MUX1 por MUX3. Os resultados foram exatamente os mesmos para a simulação.
Circuito gerado:
Baixe aqui o projeto MUX3 para o Quartus 13 ou 14.
4. MUX usando case
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX4( A, B, S, X); input A, B, S; output reg X; always @ (A or B or S) begin : MUX case(S) 1'b0 : X = A; 1'b1 : X = B; default: X = x; endcase end endmodule |
Comentários sobre o código Verilog:
O código acima também foi feito utilizando o padrão comportamental (behavioral). Utilizei um bloco always que sempre executa quando há uma mudança de qualquer uma das entradas, declaradas como input. Dentro desse bloco, usei case, que ajuda quando há diversas decisões a serem tomadas, como o switch case para o caso da linguagem C. Case faz com que testemos se determinadas condições de um sinal condizem com alguns sinais esperados. Existe apenas uma condição para default, que é a condição que cobre todos os outros sinais não descritos na lista do Case. Mais de uma condição de default causa erro de síntese do circuito. Essa condição pode ser usada para detectar erros no design, que se propagam por todo circuito, se utilizarmos esse componente de hardware junto com outros componentes, para circuitos maiores. Case é ideal principalmente quando se há muitas condições a serem atendidas.
Verifique que X é definido como reg e que o bloco case termina com endcase.
Utilizei o mesmo Testbench, trocando MUX1 por MUX4. Os resultados foram exatamente os mesmos para a simulação.
Circuito gerado:
Baixe aqui o projeto MUX4 para o Quartus 13 ou 14.
5. MUX usando lógica Digital com always
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX5( A, B, S, X ); input wire A,B; // As entradas sao A e B input wire S; // O sinal de selecao e' S output reg X; always @( A or B or S ) begin X = ( ~S & A ) | ( S & B ); end endmodule |
Comentários sobre o código Verilog:
O código acima também foi feito usando o padrão comportamental (behavioral) e é a forma mais rápida de escrever um código de forma eficiente. Usa apenas lógica digital, é bem intuitivo e fácil de entender. Na linha onde está presente a lógica da saída X em relação às entradas, o que é descrito é exatamente a expressão booleana do MUX.
1 |
X = ( ~S & A ) | ( S & B ); |
É bom lembrar novamente que pode-se usar simplesmente:
1 2 |
input A,B; // As entradas sao A e B input S; // O sinal de selecao e' S |
e
1 |
always @(*) |
Utilizei o mesmo Testbench, trocando MUX1 por MUX5. Os resultados foram exatamente os mesmos para a simulação.
Circuito gerado:
Baixe aqui o projeto MUX5 para o Quartus 13 ou 14.
6. MUX usando Tabelas com Primitivas definidas pelo usuário: UDP
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 primitive MUX6 ( X, S, A, B ) ; output X ; input S, A, B ; table // S A B X 0 1 ? : 1 ; 0 0 ? : 0 ; 1 ? 1 : 1 ; 1 ? 0 : 0 ; x 0 0 : 0 ; x 1 1 : 1 ; endtable endprimitive |
Comentários sobre o código Verilog:
Nesse tipo de implementação, é necessário que se defina todos os possíveis estados das entradas em forma de tabela e o que acontecerá em cada um dos casos com as saídas. Vamos analisar a primeira linha a tabela:
1 |
0 1 ? : 1 ; |
Se a entrada S for iguaal a zero, a entrada A for 1, X receberá o valor 1, independente do valor de B, que está representado por uma exclamação. A diretiva dois pontos separa as entradas das saídas.
Veja que quando queremos utilizar tabelas UDP, precisamos começar o código com a palavra primitive.
Testbench:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
module MUX6_TB; wire X_tb; reg S_tb, A_tb, B_tb; integer i; MUX6 dut( X_tb, S_tb, A_tb, B_tb); initial begin A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b0; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b1; #1 $display("A = %x, B = %x, S = %x, X = %x", A_tb, B_tb, S_tb, X_tb); end endmodule |
Os resultados foram exatamente os mesmos para a simulação.
Circuito gerado:
Baixe aqui o projeto MUX6 para o Quartus 13 ou 14.
7. MUX usando apenas portas lógicas NAND
Representação do Circuito em Verilog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Embarcados - Use como quiser e de os creditos // Exemplo de Implementacao de um Multiplex 2 pra 1 // Thiago Lima - 14/11/2015 module MUX7( A, B, S, X); input wire A, B; // As entradas sao A e B input wire S; // O sinal de selecao é S output wire X; // O sinal de saida é X wire S0_inv, a1, b1; nand u1( S0_inv, S, S ); nand u2( a1, S0_inv, A ); nand u3( b1, S, B ); nand u4( X, a1, b1 ); endmodule |
Comentários sobre o código Verilog:
O código acima é implementado apenas com portas lógicas, conforme a sintaxe mostrada no primeiro exemplo. É a implementação mais rápida quando feita em um ASIC devido à construção das portas NAND.
Testbench:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
module MUX7_TB; reg A_tb, B_tb, S_tb; wire X_tb; integer i; MUX7 dut( A_tb, B_tb, S_tb, X_tb); initial begin A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b0; #1 A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b0; #1 A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b0; #1 A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b0; #1 A_tb = 1'b0; B_tb = 1'b0; S_tb = 1'b1; #1 A_tb = 1'b0; B_tb = 1'b1; S_tb = 1'b1; #1 A_tb = 1'b1; B_tb = 1'b0; S_tb = 1'b1; #1 A_tb = 1'b1; B_tb = 1'b1; S_tb = 1'b1; #1; end endmodule |
Onda de Saída:
Baixe aqui o projeto MUX7 para o Quartus 13 ou 14.
Para aprender mais sobre Verilog
- PROCESSADORES PROGRAMÁVEIS – como projetar um processador em VERILOG
- Formas de representar um sistema digital
- Tutorial de Modelsim: Verificando o VHDL antes de programar o FPGA
E isso é tudo pessoal, espero que tenham gostado dessas 7 dicas de hardware e adoraria de ouvir de vocês como posso melhorar os próximos textos. Mandem feedbacks, críticas, enfim, deixem seus comentários abaixo.