Galeria de projetos e testbenches

Esta galeria reúne exemplos pequenos para praticar o fluxo completo de simulação na AURORA. Cada circuito ou algoritmo possui duas alternativas de teste: um testbench Verilog (.v) e um testbench cocotb (.py). Use apenas uma alternativa como Testbench Top em cada execução.

Como usar os exemplos

Para um exemplo Verilog:

  1. Baixe ou copie os três arquivos do conjunto para a pasta do projeto.

  2. Adicione o arquivo do circuito como fonte sintetizável.

  3. Defina o arquivo do circuito como Top Level.

  4. Adicione tb_*.v ou test_*.py como testbench.

  5. Defina o testbench escolhido como Testbench Top.

  6. Execute Analisar Verilog (forma de onda) para abrir as formas de onda ou Execução rápida para conferir apenas as verificações.

Para um exemplo C±:

  1. Crie no Hub de Processadores um processador com o mesmo nome indicado por #PRNAME.

  2. Substitua o conteúdo do arquivo em Software pelo exemplo .cmm.

  3. Execute para gerar o módulo em Hardware/<nome>.v e os arquivos de memória necessários.

  4. Adicione o testbench .v ou .py correspondente ao projeto.

  5. Defina Hardware/<nome>.v como Top Level e o teste escolhido como Testbench Top.

  6. Execute Analisar Verilog (forma de onda) ou Execução rápida.

Importante

Os testbenches dos exemplos C± exercitam o Verilog gerado pelo compilador. Compile o .cmm antes da simulação e mantenha os arquivos gerados pela AURORA no projeto. O arquivo Python não executa C± diretamente.

Porta AND em Verilog

Este primeiro conjunto cobre um circuito combinacional. Os quatro pares possíveis de entrada são aplicados, e cada saída é comparada com a tabela-verdade da operação AND.

Arquivos: porta_and.v, tb_porta_and.v e test_porta_and.py.

Circuito porta_and.v

1module porta_and (
2    input  wire a,
3    input  wire b,
4    output wire y
5);
6    assign y = a & b;
7endmodule

Testbench Verilog tb_porta_and.v

 1`timescale 1ns/1ps
 2
 3module tb_porta_and;
 4    reg a;
 5    reg b;
 6    wire y;
 7
 8    porta_and dut (
 9        .a(a),
10        .b(b),
11        .y(y)
12    );
13
14    task verificar;
15        input valor_a;
16        input valor_b;
17        input esperado;
18        begin
19            a = valor_a;
20            b = valor_b;
21            #1;
22
23            if (y !== esperado) begin
24                $display(
25                    "ERRO: a=%0b b=%0b esperado=%0b obtido=%0b",
26                    a, b, esperado, y
27                );
28                $fatal(1);
29            end
30        end
31    endtask
32
33    initial begin
34        $dumpfile("tb_porta_and.vcd");
35        $dumpvars(0, tb_porta_and);
36
37        verificar(1'b0, 1'b0, 1'b0);
38        verificar(1'b0, 1'b1, 1'b0);
39        verificar(1'b1, 1'b0, 1'b0);
40        verificar(1'b1, 1'b1, 1'b1);
41
42        $display("SUCESSO: tabela verdade validada.");
43        $finish;
44    end
45endmodule

O bloco task verificar evita repetir a sequência de atribuição, espera e comparação. $fatal(1) encerra a execução com falha assim que um caso produz uma saída incorreta. $dumpfile e $dumpvars registram os sinais para o viewer de ondas.

Testbench cocotb test_porta_and.py

 1# aurora-toplevel: porta_and
 2
 3import cocotb
 4from cocotb.triggers import Timer
 5
 6
 7async def verificar(dut, valor_a, valor_b, esperado):
 8    dut.a.value = valor_a
 9    dut.b.value = valor_b
10    await Timer(1, unit="ns")
11
12    obtido = int(dut.y.value)
13    assert obtido == esperado, (
14        f"a={valor_a} b={valor_b}: esperado={esperado}, obtido={obtido}"
15    )
16
17
18@cocotb.test()
19async def testa_tabela_verdade(dut):
20    casos = [
21        (0, 0, 0),
22        (0, 1, 0),
23        (1, 0, 0),
24        (1, 1, 1),
25    ]
26
27    for valor_a, valor_b, esperado in casos:
28        await verificar(dut, valor_a, valor_b, esperado)

A diretiva # aurora-toplevel: porta_and seleciona explicitamente o módulo testado. A função auxiliar aplica um caso, aguarda a propagação combinacional e inclui entradas, valor esperado e valor obtido na mensagem da asserção.

Contador de 4 bits em Verilog

Este conjunto introduz clock, reset e controle de habilitação. O teste confirma que o reset zera o estado, que o contador avança a cada borda quando enable vale 1 e que o valor permanece estável quando enable vale 0.

Arquivos: contador4.v, tb_contador4.v e test_contador4.py.

Circuito contador4.v

 1module contador4 (
 2    input  wire       clk,
 3    input  wire       rst,
 4    input  wire       enable,
 5    output reg  [3:0] valor
 6);
 7    always @(posedge clk) begin
 8        if (rst)
 9            valor <= 4'd0;
10        else if (enable)
11            valor <= valor + 4'd1;
12    end
13endmodule

Testbench Verilog tb_contador4.v

 1`timescale 1ns/1ps
 2
 3module tb_contador4;
 4    reg clk = 1'b0;
 5    reg rst = 1'b1;
 6    reg enable = 1'b0;
 7    wire [3:0] valor;
 8
 9    contador4 dut (
10        .clk(clk),
11        .rst(rst),
12        .enable(enable),
13        .valor(valor)
14    );
15
16    always #5 clk = ~clk;
17
18    initial begin
19        $dumpfile("tb_contador4.vcd");
20        $dumpvars(0, tb_contador4);
21
22        repeat (2) @(posedge clk);
23        @(negedge clk);
24        rst = 1'b0;
25        enable = 1'b1;
26
27        repeat (4) @(posedge clk);
28        #1;
29        if (valor !== 4'd4) begin
30            $display("ERRO: esperado=4 obtido=%0d", valor);
31            $fatal(1);
32        end
33
34        @(negedge clk);
35        enable = 1'b0;
36        repeat (2) @(posedge clk);
37        #1;
38        if (valor !== 4'd4) begin
39            $display("ERRO: contador mudou com enable=0. obtido=%0d", valor);
40            $fatal(1);
41        end
42
43        $display("SUCESSO: reset, contagem e enable validados.");
44        $finish;
45    end
46endmodule

O clock alterna a cada 5 ns, portanto o período é de 10 ns. O reset é retirado na borda de descida para estar estável antes da próxima borda de subida. O atraso #1 após a borda permite observar o valor atualizado pela atribuição não bloqueante do contador.

Testbench cocotb test_contador4.py

 1# aurora-toplevel: contador4
 2
 3import cocotb
 4from cocotb.clock import Clock
 5from cocotb.triggers import FallingEdge, ReadOnly, RisingEdge
 6
 7
 8async def ler_apos_borda(dut):
 9    await RisingEdge(dut.clk)
10    await ReadOnly()
11    return int(dut.valor.value)
12
13
14@cocotb.test()
15async def testa_reset_contagem_e_enable(dut):
16    cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
17
18    dut.rst.value = 1
19    dut.enable.value = 0
20    await ler_apos_borda(dut)
21    assert await ler_apos_borda(dut) == 0, "O reset nao zerou o contador."
22
23    await FallingEdge(dut.clk)
24    dut.rst.value = 0
25    dut.enable.value = 1
26    for esperado in range(1, 5):
27        obtido = await ler_apos_borda(dut)
28        assert obtido == esperado, (
29            f"Contagem incorreta: esperado={esperado}, obtido={obtido}"
30        )
31
32    await FallingEdge(dut.clk)
33    dut.enable.value = 0
34    for _ in range(2):
35        obtido = await ler_apos_borda(dut)
36        assert obtido == 4, (
37            f"O contador mudou com enable=0: esperado=4, obtido={obtido}"
38        )

Clock gera o clock em uma corrotina separada. A função ler_apos_borda aguarda RisingEdge e depois ReadOnly, fase em que as atualizações daquele instante já podem ser lidas sem disputar com a lógica do simulador. As mudanças de rst e enable são feitas após FallingEdge, fora da fase somente de leitura e antes da próxima borda ativa.

Soma de constantes em C±

Este exemplo gera um processador chamado soma_constantes. O algoritmo soma 3 + 4, escreve 7 na porta de saída 0 e alcança #TOAQUI, que permite ao hardware indicar o término pelo sinal cheguei.

Arquivos: soma_constantes.cmm, tb_soma_constantes.v e test_soma_constantes.py.

Algoritmo soma_constantes.cmm

 1#PRNAME soma_constantes
 2#NUBITS 32
 3#NBMANT 23
 4#NBEXPO 8
 5#NDSTAC 8
 6#SDEPTH 8
 7#NUIOIN 1
 8#NUIOOU 1
 9#NUGAIN 128
10
11void main()
12{
13    int a = 3;
14    int b = 4;
15    int resultado;
16
17    resultado = a + b;
18    out(0, resultado);
19
20    #TOAQUI
21}

Testbench Verilog tb_soma_constantes.v

 1`timescale 1ns/1ps
 2
 3module tb_soma_constantes;
 4    reg clk = 1'b0;
 5    reg rst = 1'b1;
 6    wire signed [31:0] out;
 7    wire [0:0] out_en;
 8    wire cheguei;
 9
10    integer ciclos = 0;
11    integer recebeu_saida = 0;
12    integer terminou = 0;
13
14    soma_constantes dut (
15        .clk(clk),
16        .rst(rst),
17        .out(out),
18        .out_en(out_en),
19        .cheguei(cheguei)
20    );
21
22    always #5 clk = ~clk;
23
24    always @(posedge clk) begin
25        if (!rst && (out_en == 1)) begin
26            recebeu_saida = 1;
27            if ($signed(out) !== 32'sd7) begin
28                $display("ERRO: esperado=7 obtido=%0d", $signed(out));
29                $fatal(1);
30            end
31        end
32
33        if (cheguei === 1'b1)
34            terminou = 1;
35    end
36
37    initial begin
38        $dumpfile("tb_soma_constantes.vcd");
39        $dumpvars(0, tb_soma_constantes);
40
41        repeat (2) @(posedge clk);
42        @(negedge clk);
43        rst = 1'b0;
44
45        while (!terminou && (ciclos < 200)) begin
46            @(posedge clk);
47            ciclos = ciclos + 1;
48        end
49
50        repeat (2) @(posedge clk);
51        #1;
52
53        if (!recebeu_saida) begin
54            $display("ERRO: nenhuma escrita foi observada na porta 0.");
55            $fatal(1);
56        end
57
58        if (!terminou) begin
59            $display("ERRO: tempo limite antes de #TOAQUI.");
60            $fatal(1);
61        end
62
63        $display("SUCESSO: saida 7 e termino do algoritmo validados.");
64        $finish;
65    end
66endmodule

O bloco acionado na borda de subida observa out_en == 1, condição que identifica uma escrita na porta 0. A captura ocorre na própria borda porque esse sinal funciona como um pulso de habilitação da escrita. O limite de 200 ciclos evita uma simulação infinita se o algoritmo não produzir a saída ou não alcançar #TOAQUI.

Testbench cocotb test_soma_constantes.py

 1# aurora-toplevel: soma_constantes
 2
 3import cocotb
 4from cocotb.clock import Clock
 5from cocotb.triggers import FallingEdge, ReadOnly, RisingEdge
 6
 7
 8@cocotb.test()
 9async def testa_soma_e_termino(dut):
10    cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
11
12    dut.rst.value = 1
13    for _ in range(2):
14        await RisingEdge(dut.clk)
15    await FallingEdge(dut.clk)
16    dut.rst.value = 0
17
18    saidas = []
19    ciclos_apos_termino = 0
20    for _ in range(200):
21        await FallingEdge(dut.clk)
22        await ReadOnly()
23
24        if int(dut.out_en.value) == 1:
25            saidas.append(int(dut.out.value))
26
27        if int(dut.cheguei.value) == 1 and ciclos_apos_termino == 0:
28            ciclos_apos_termino = 1
29        elif ciclos_apos_termino:
30            ciclos_apos_termino += 1
31
32        if ciclos_apos_termino >= 3:
33            break
34    else:
35        raise AssertionError("Tempo limite antes de o algoritmo atingir #TOAQUI.")
36
37    assert saidas == [7], f"Esperado [7] na porta 0, obtido {saidas}."

O teste amostra out_en, out e cheguei na borda de descida do clock, quando os sinais produzidos na borda ativa já estão estáveis. Ele exige a lista exata [7]; dessa forma, detecta ausência de saída, valor incorreto e escritas adicionais inesperadas. O limite de ciclos impede uma espera infinita.

Sequência de quadrados em C±

Este exemplo usa um laço while para escrever 0, 1, 4 e 9 na porta 0. Além do valor de cada amostra, os testes verificam a ordem e a quantidade de escritas.

Arquivos: sequencia_quadrados.cmm, tb_sequencia_quadrados.v e test_sequencia_quadrados.py.

Algoritmo sequencia_quadrados.cmm

 1#PRNAME sequencia_quadrados
 2#NUBITS 32
 3#NBMANT 23
 4#NBEXPO 8
 5#NDSTAC 8
 6#SDEPTH 8
 7#NUIOIN 1
 8#NUIOOU 1
 9#NUGAIN 128
10
11void main()
12{
13    int indice = 0;
14
15    while (indice < 4) {
16        out(0, indice * indice);
17        indice = indice + 1;
18    }
19
20    #TOAQUI
21}

Testbench Verilog tb_sequencia_quadrados.v

 1`timescale 1ns/1ps
 2
 3module tb_sequencia_quadrados;
 4    reg clk = 1'b0;
 5    reg rst = 1'b1;
 6    wire signed [31:0] out;
 7    wire [0:0] out_en;
 8    wire cheguei;
 9
10    integer ciclos = 0;
11    integer indice = 0;
12    integer terminou = 0;
13    integer esperado [0:3];
14
15    sequencia_quadrados dut (
16        .clk(clk),
17        .rst(rst),
18        .out(out),
19        .out_en(out_en),
20        .cheguei(cheguei)
21    );
22
23    always #5 clk = ~clk;
24
25    always @(posedge clk) begin
26        if (!rst && (out_en == 1)) begin
27            if (indice > 3) begin
28                $display("ERRO: o processador produziu saidas extras.");
29                $fatal(1);
30            end
31
32            if ($signed(out) !== esperado[indice]) begin
33                $display(
34                    "ERRO: indice=%0d esperado=%0d obtido=%0d",
35                    indice, esperado[indice], $signed(out)
36                );
37                $fatal(1);
38            end
39
40            indice = indice + 1;
41        end
42
43        if (cheguei === 1'b1)
44            terminou = 1;
45    end
46
47    initial begin
48        esperado[0] = 0;
49        esperado[1] = 1;
50        esperado[2] = 4;
51        esperado[3] = 9;
52
53        $dumpfile("tb_sequencia_quadrados.vcd");
54        $dumpvars(0, tb_sequencia_quadrados);
55
56        repeat (2) @(posedge clk);
57        @(negedge clk);
58        rst = 1'b0;
59
60        while (!terminou && (ciclos < 400)) begin
61            @(posedge clk);
62            ciclos = ciclos + 1;
63        end
64
65        repeat (2) @(posedge clk);
66        #1;
67
68        if (indice !== 4) begin
69            $display("ERRO: esperadas=4 saidas_recebidas=%0d", indice);
70            $fatal(1);
71        end
72
73        if (!terminou) begin
74            $display("ERRO: tempo limite antes de #TOAQUI.");
75            $fatal(1);
76        end
77
78        $display("SUCESSO: sequencia 0, 1, 4, 9 validada.");
79        $finish;
80    end
81endmodule

O vetor esperado funciona como um modelo de referência simples. Cada pulso de out_en consome uma posição. O teste falha se houver valor incorreto, quantidade diferente de quatro saídas ou ausência do sinal de término.

Testbench cocotb test_sequencia_quadrados.py

 1# aurora-toplevel: sequencia_quadrados
 2
 3import cocotb
 4from cocotb.clock import Clock
 5from cocotb.triggers import FallingEdge, ReadOnly, RisingEdge
 6
 7
 8@cocotb.test()
 9async def testa_sequencia_e_termino(dut):
10    cocotb.start_soon(Clock(dut.clk, 10, unit="ns").start())
11
12    dut.rst.value = 1
13    for _ in range(2):
14        await RisingEdge(dut.clk)
15    await FallingEdge(dut.clk)
16    dut.rst.value = 0
17
18    saidas = []
19    ciclos_apos_termino = 0
20    for _ in range(400):
21        await FallingEdge(dut.clk)
22        await ReadOnly()
23
24        if int(dut.out_en.value) == 1:
25            saidas.append(int(dut.out.value))
26
27        if int(dut.cheguei.value) == 1 and ciclos_apos_termino == 0:
28            ciclos_apos_termino = 1
29        elif ciclos_apos_termino:
30            ciclos_apos_termino += 1
31
32        if ciclos_apos_termino >= 3:
33            break
34    else:
35        raise AssertionError("Tempo limite antes de o algoritmo atingir #TOAQUI.")
36
37    assert saidas == [0, 1, 4, 9], (
38        f"Esperado [0, 1, 4, 9] na porta 0, obtido {saidas}."
39    )

A lista Python torna a comparação da sequência inteira direta. Esse padrão pode ser ampliado para filtros, máquinas de estados e processadores que produzam séries de amostras.

Escolher entre os dois tipos de testbench

Situação

Testbench recomendado

Aprender eventos e tarefas do Verilog

.v

Controlar diretamente o dump de sinais

.v

Percorrer muitos casos de entrada

.py com cocotb

Criar listas, modelos matemáticos e mensagens detalhadas

.py com cocotb

Depurar a integração inicial de um módulo

Comece com .v e reproduza em .py quando necessário

Os dois formatos verificam o mesmo hardware. A escolha altera a linguagem e a organização do teste, não o circuito sintetizável. Para o procedimento de execução e o diagnóstico de falhas, consulte Simular com Icarus, Verilator ou cocotb.