Tutorial: um contador em Verilog

Este é o primeiro tutorial do manual. Ao final, você terá criado um projeto, escrito um módulo Verilog e um testbench, validado o design, simulado e lido o resultado na forma de onda. Reserve uns vinte minutos.

O que vamos construir

Um contador de 4 bits com habilitação: conta a cada borda de clock enquanto habilita estiver em 1, e zera no reset. É o circuito sequencial mais simples que exercita o fluxo inteiro: clock, reset, registrador e testbench.

Passo 1: criar o projeto

  1. Clique em Novo Projeto, na barra superior ou na tela de boas-vindas.

  2. Nomeie LabContador. O nome aceita letras, números, sublinhado e hífen; sem espaços nem acentos.

  3. Clique em Procurar, escolha uma pasta com permissão de escrita e clique em Gerar Projeto.

Modal Novo Projeto preenchido.

Figura 4 O formulário pede só nome e local. O campo de local é preenchido pelo botão Procurar.

Confira: a árvore mostra LabContador, vazio, e a barra de status passou a Pronto.

Passo 2: escrever o módulo

  1. Na árvore, clique com o botão direito na área vazia e escolha Novo arquivo.

  2. Nomeie contador.v e salve dentro da pasta do projeto.

  3. Digite o módulo:

Listagem 1 contador.v
 1module contador (
 2    input  wire       clk,
 3    input  wire       rst,
 4    input  wire       habilita,
 5    output reg  [3:0] conta
 6);
 7
 8    always @(posedge clk) begin
 9        if (rst)
10            conta <= 4'd0;
11        else if (habilita)
12            conta <= conta + 4'd1;
13    end
14
15endmodule

Salve com Ctrl+S.

Editor com o contador em Verilog e diagnósticos ao vivo.

Figura 5 Enquanto você digita, dois analisadores conferem o código: erros de sintaxe aparecem sublinhados na hora, e a análise semântica aponta sinais não declarados e portas incompatíveis.

Confira: na visão Arquivos, contador.v apareceu com o ícone de fonte sintetizável. A AURORA o classificou sozinha, lendo o conteúdo: sem initial, sem $finish, é circuito.

Passo 3: escrever o testbench

Crie um segundo arquivo, tb_contador.v:

Listagem 2 tb_contador.v
 1`timescale 1ns/1ps
 2
 3module tb_contador;
 4
 5    reg        clk = 0;
 6    reg        rst = 1;
 7    reg        habilita = 0;
 8    wire [3:0] conta;
 9
10    contador dut (
11        .clk      (clk),
12        .rst      (rst),
13        .habilita (habilita),
14        .conta    (conta)
15    );
16
17    always #5 clk = ~clk;      // clock de 100 MHz
18
19    initial begin
20        $dumpfile("tb_contador.fst");
21        $dumpvars(0, tb_contador);
22
23        #12 rst = 0;           // solta o reset fora da borda
24        #8  habilita = 1;      // conta por 20 ciclos
25        #200 habilita = 0;     // congela
26        #40 $finish;
27    end
28
29endmodule

Repare nos ingredientes que todo testbench tem: o clock gerado por always #5, o reset solto depois de alguns nanossegundos, o $dumpvars que grava os sinais para a forma de onda e o $finish que encerra a simulação.

Confira: o arquivo entrou na árvore com o ícone de testbench. initial, $finish e o nome começando com tb_ denunciaram a categoria.

Passo 4: definir os papéis

  1. Botão direito em contador.v, escolha Definir como Top Level.

  2. Botão direito em tb_contador.v, escolha Marcar como Testbench.

Confira: a barra de status agora mostra os dois nomes, e os botões Sintetizar Verilog e Analisar Verilog acenderam.

Passo 5: validar

Clique em Sintetizar Verilog.

A AURORA elabora o projeto inteiro a partir do Top Level: resolve os módulos, confere portas e conexões, e monta a hierarquia de instâncias. Não é a síntese física do FPGA; é a prova de que o design fecha.

Terminal TVERI com a validação bem-sucedida e a visão Hierarquia habilitada.

Figura 6 O terminal TVERI relata a validação. Depois dela, o botão de visões da árvore ganha a opção Hierarquia.

Confira: alterne a árvore para a visão Hierarquia. Deve aparecer a instância do contador. Clicar nela abre o fonte na linha da definição.

Passo 6: simular e ler a onda

  1. Confirme na barra de ferramentas: simulador Icarus Verilog, visualizador GTKWave.

  2. Clique em Analisar Verilog.

A AURORA compila os fontes com o testbench, roda a simulação e abre o GTKWave com os sinais já organizados. Acompanhe o terminal TWAVE.

GTKWave com clock, reset, habilita e a contagem subindo.

Figura 7 A forma de onda do contador: o reset solta, habilita sobe e conta incrementa a cada borda de clock, de 0 a 15 e de volta a 0.

Procure na onda:

  • rst alto no início e a contagem presa em 0;

  • conta subindo um a um a cada borda de subida do clock enquanto habilita está em 1;

  • o estouro: depois de 15, o contador de 4 bits volta a 0;

  • a contagem congelada quando habilita cai.

O que você aprendeu

  • Um projeto é uma pasta com um .spf; os fontes entram por criação ou arraste.

  • A AURORA separa circuito de testbench sozinha, pelo conteúdo do arquivo.

  • Top Level e Testbench Top são os dois papéis que você marca, e a barra de status os exibe sempre.

  • Sintetizar valida a elaboração e constrói a hierarquia; Analisar simula e abre a onda.

  • O testbench manda na simulação: clock, reset, estímulos, $dumpvars e $finish são seus.

Um segundo padrão: máquina de estados

O contador é o primeiro padrão sequencial; o segundo é a máquina de estados finitos, e vale construí-la já. Um semáforo simples: verde, amarelo, vermelho, cada um com sua duração.

Crie semaforo.v no mesmo projeto:

Listagem 3 semaforo.v
 1module semaforo (
 2    input  wire       clk,
 3    input  wire       rst,
 4    output reg  [2:0] luz        // {verde, amarelo, vermelho}
 5);
 6
 7    localparam VERDE    = 2'd0;
 8    localparam AMARELO  = 2'd1;
 9    localparam VERMELHO = 2'd2;
10
11    localparam T_VERDE    = 4'd8;
12    localparam T_AMARELO  = 4'd2;
13    localparam T_VERMELHO = 4'd6;
14
15    reg [1:0] estado;
16    reg [3:0] tempo;
17
18    always @(posedge clk) begin
19        if (rst) begin
20            estado <= VERDE;
21            tempo  <= 4'd0;
22        end else begin
23            tempo <= tempo + 4'd1;
24            case (estado)
25                VERDE:    if (tempo == T_VERDE    - 1) begin estado <= AMARELO;  tempo <= 0; end
26                AMARELO:  if (tempo == T_AMARELO  - 1) begin estado <= VERMELHO; tempo <= 0; end
27                VERMELHO: if (tempo == T_VERMELHO - 1) begin estado <= VERDE;    tempo <= 0; end
28                default:  estado <= VERDE;
29            endcase
30        end
31    end
32
33    always @(*) begin
34        case (estado)
35            VERDE:    luz = 3'b100;
36            AMARELO:  luz = 3'b010;
37            default:  luz = 3'b001;
38        endcase
39    end
40
41endmodule

O padrão em três blocos: os estados nomeados com localparam, um always sequencial com o registrador de estado e o temporizador, e um always combinacional com a saída em função do estado. Escreva um testbench nos moldes do anterior (solte o reset e deixe rodar uns 200 ciclos), marque os papéis e observe na onda o ciclo verde, amarelo, vermelho se repetindo. A visão Hierarquia agora mostra dois módulos de topo possíveis; o Top Level marca qual vale.

Exercícios

  1. Faça o contador contar de 0 a 9 e reiniciar, virando um contador de década.

  2. Acrescente um pino sobe_desce ao contador: 1 conta para cima, 0 para baixo. Confirme na onda os dois sentidos.

  3. Dê ao contador uma saída estouro, em 1 apenas no ciclo em que a contagem volta a zero. Grave-a na onda.

  4. No semáforo, acrescente um pino de pedestre que, pressionado durante o verde, encurta o tempo restante para no máximo 2 ciclos. Cuidado com o caso do tempo já estar abaixo disso.

Para onde ir

As formas de onda em detalhe estão em Formas de onda, o fluxo em O fluxo Verilog em detalhe e os testbenches, incluindo cocotb, em Testbenches: Verilog e cocotb. Quando quiser gerar um processador em vez de escrever o circuito à mão, siga para a Parte III: Tutorial: um processador SAPHO.