Ajuda testbench Verilog

Linguagem descritiva de hardware

Moderadores: 51, guest2003

Ajuda testbench Verilog

Mensagempor chrdcv » 12 Jun 2016 00:44

Pessoal, estou pastando aqui para bolar um testbench para a descrição em verilog. Alguém poderia ajudar-me por gentileza? Minha idéia é gerar as primeiras 512 combinações de cada entrada.

A descrição nada mais é do que uma implementação de GCD utilizando o algoritmo de Euclides, mas usando a diferença ao invés do módulo:

Código: Selecionar todos
module gcd_engine (
    input wire clk, reset,
    input wire start,
    input wire [8:0] a_in, b_in,
    output wire gcd_done_tick, ready,
    output wire [8:0] r
  );


  // symbolic state declaration:
  localparam[1:0]
    idle = 2'b01,
    op   = 2'b10;

  // signal declaration:
  reg [1:0] state_reg, state_next;
  reg [8:0] a_reg, a_next, b_reg, b_next;
  reg [4:0] n_reg, n_next;
  reg gcd_done;

  // Body:
  // FSMD state and data registers:
  always @(posedge clk, posedge reset) begin
    if(reset) begin
      state_reg <= idle;
      a_reg <= 0;
      b_reg <= 0;
      n_reg <= 0;
    end else begin
      state_reg <= state_next;
      a_reg <= a_next;
      b_reg <= b_next;
      n_reg <= n_next;
    end
  end

  // next-state logic and data path functional unit:
  always @(*) begin
    a_next = a_reg;
    b_next = b_reg;
    n_next = n_reg;
    state_next = state_reg;
    gcd_done = 1'b0;
    case(state_reg)
      idle:
        if(start) begin
          a_next = a_in;
          b_next = b_in;
          n_next = 0;
          state_next = op;
        end
      op:
        if(a_reg == b_reg) begin
          state_next = idle;
          gcd_done = 1'b1;
          a_next = a_reg << n_reg;
        end else
          if(~a_reg[0]) begin
            a_next = {1'b0, a_reg[8:1]};
            if(~b_reg[0]) begin
              b_next = {1'b0, b_reg[8:1]};
              n_next = n_reg + 1;
            end
          end else
            if(~b_reg[0])
              b_next = {1'b0, b_reg[8:1]};
            else
              if(a_reg > b_reg)
                a_next = a_reg - b_reg;
              else
                b_next = b_reg - a_reg;
    endcase
  end
 
  // output:
  assign ready = (state_reg == idle);
  assign r = a_reg;
  assign gcd_done_tick = gcd_done;
endmodule



Tentei fazer um testbench mas o resultado não saiu como esperado:
Código: Selecionar todos
// `timescale <unit>/<precision>
`timescale 1ns/100ps

module gcd_tb();
  reg  clk, rst, start;
  reg  [9:0]i,j;
  wire [8:0]y;
  wire done_tick, ready;

  // Instantiate the Device Under Test (DUT):
  gcd_engine gcd_engine_dut0(clk, rst, start, i, j, done_tick, ready, y);

  // Clock:
  always begin
    #10 clk = ~clk;
  end

  // Initial sequential block simulation:
  initial begin
    rst = 1'b1;
    clk = 1'b0;
    #20 rst = 1'b0;
    #5 start = 1'b1;
    i = 10;
    j = 14;
    #5 start = 1'b0;
  end

  always @(posedge clk) begin
    @(ready == 1'b1);
    wait(ready == 1'b0);
    #5 start = 1'b0;

    j = j + 1;
    if(j > 10) begin
      i = i + 1;
      j = 1;     
      if(i > 15) begin
        i = 1;
        $finish;
      end
    end

    #5 start = 1'b1;

    //wait(ready == 1'b0);
    @(done_tick == 1'b1);
    $display("%d %d %d", i, j, y);
  end

  initial begin
    $dumpfile("gcd_tb.vcd");
    $dumpvars;
  end

endmodule


Sei que o algoritmo e a implementação do mesmo na descrição estão corretos, pois foi extraído do livro do Pong Chu. O que está errado é o meu TestBench.
Seu Madruga: "O trabalho não é ruim, ruim é ter que trabalhar"
Avatar do usuário
chrdcv
Dword
 
Mensagens: 1580
Registrado em: 13 Out 2006 14:13

Re: Ajuda testbench Verilog

Mensagempor msamsoniuk » 12 Jun 2016 02:23

vc estah usando que software para testar isso? eu rodei o seu testbench no webpack 10.1 da xilinx e gerou o seguinte resultado:

Imagem
https://darklife.org/marcelo/public/xil ... .16.54.png

bom, nao conheco o algoritmo e nao sei dizer se estah rodando correto ou nao, mas o simulador indicou que os sinais start, i e j estao indeterminados logo no inicio e precisam ser inicializados em t=0. no caso:

Código: Selecionar todos
  initial begin
    rst = 1'b1;
    clk = 1'b0;
    i = 0;
    j = 0;
    start = 1'b0;
    #20 rst = 1'b0;
    #5 start = 1'b1;
    i = 10;
    j = 14;
    #5 start = 1'b0;
  end


tambem apareceram dois warnings:

WARNING:Simulator:679 - For instance asm51/gcd_engine_dut0/, width 9 of formal port a_in is not equal to width 10 of actual variable i.
WARNING:Simulator:679 - For instance asm51/gcd_engine_dut0/, width 9 of formal port b_in is not equal to width 10 of actual variable j.


referente ao tamanho da variavel i,j ser [9:0] e os inputs no modulo serem [8:0]. note que o verilog eh tipo C: ele nao liga e supo que vc saiba o que esta fazendo! :D

bom, isso eh o diagnostico que o software da xilinx gerou. observando o resultado, porem, eu nao posso deixar de comentar: os sinais start,i e j estao mudando de forma assincrona. a menos que isto esteja simulando algum comportamento externo de um CI que eh realmente assincrono, acredito que o correto seria escrever o testbench de modo que start, i e j mudam de forma sincrona com a borda de subida do clock.

normalmente eu escrevo meus testbenchs de forma totalmente sincrona, ficando apenas o clock assincrono:

Código: Selecionar todos
module gcd_tb2;

  reg  clk, rst, start;
  reg  [9:0]i,j;
  wire [8:0]y;
  wire done_tick, ready;

  // Instantiate the Device Under Test (DUT):
  gcd_engine gcd_engine_dut0(clk, rst, start, i, j, done_tick, ready, y);

  // Clock:
  always begin
    #10 clk = ~clk;
  end

  // Initial sequential block simulation:
  initial begin
    rst = 1'b1;
    clk = 1'b0;
    #25 rst = 1'b0;
    //#5 start = 1'b1;
    //i = 10;
    //j = 14;
    //#5 start = 1'b0;
  end

  always @(posedge clk)
  begin
    if(rst) // costumo usar o reset desta forma
    begin
        start <= 1'b1;
        i <= 10;
        j <= 14;
    end
    else
    if(ready) // aqui ele condiciona o ready
    begin
        start <= 1'b0;

        if(j > 10) begin
          i <= i + 1;
          j <= 1;     
          if(i > 15) begin
            i <= 1;
            $finish;
          end
        end
        else         
          j <= j + 1;

    end
    else // aqui sem ready... fica magicamente correto!
        start <= 1'b1;

    //wait(ready == 1'b0);
    if(done_tick)
        $display("%d %d %d", i, j, y);
  end
     
endmodule


acredito eu que fica o mesmo resultado, mas alguns sinais internos ficam mais limpos, sem estados intermediarios malucos decorrendos de sinais assincronos que mudam as entrada:

Imagem
https://darklife.org/marcelo/public/xil ... .16.54.png

novamente: como nao conheco o algoritmo, nao sei dizer se o resultado estah correto ou nao.

ah! se as figuras nao carregarem, entre na URL, provavelmente o browser vai reclamar alguma coisa sobre certificado expirado... mas estah ok, pode mandar aceitar: o certificado do HTTPS estah expirado mesmo faz decadas! :D hahaha
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04

Re: Ajuda testbench Verilog

Mensagempor chrdcv » 12 Jun 2016 19:23

Marcelo,

Agradeço pela cordialidade e presteza ao responder a minha mensagem! :mrgreen: Esse é um exercício de Introdução aos Sistemas Lógicos aqui da UFMG e o professor não teve muito tempo para lecionar Verilog. O monitor da disciplina simplesmente deu uma introdução à linguagem em uma aula de 01:40h e depois solicitou que fizéssemos dois trabalhos. O primeiro era de fatoração de inteiros e o segundo o Máximo Divisor Comum (GCD): (https://en.wikipedia.org/wiki/Euclidean_algorithm) . Optei por começar a fazer o segundo primeiro, por considerar sendo mais fácil.

Disponho de uma máquina de terceira mão aqui em casa, então, uso o Icarus Verilog por ser leve e na escola, uso o Quartus, sendo a placa de uso uma Altera DE2: www.terasic.com.tw/cgi-bin/page/archive.pl?CategoryNo=53&No=30&PartNo=4

A implementação do algoritmo do Máximo Divisor Comum em C seria algo do tipo:
Código: Selecionar todos
#include <stdio.h>
#include <string.h>

int gcd_dif (int a, int b) {
  while(a != b)
    if(a > b) a -= b;
    else b -= a;
  return a;
}

int main (int argc, char **argv) {
  for(int i = 1; i < 512; i++) {
    for(int j = 1; j < 512; j++) {
      printf("%4d %4d %3d\n", i, j, gcd_dif(i, j));     
    }
  }

  return 0;
}


Como dito anteriormente, a implementação Euclideana do GCD em Verilog é baseada totalmente no livro do Pong Chu (https://books.google.com.br/books?id=LEy0k2RFv8kC&pg=PA663&lpg=PA663&dq=pong+chu+gcd&source=bl&ots=Xf7KH1uAfZ&sig=jbQqMKHuVXvcvvpwylC1p0xtLgU&hl=en&sa=X&ved=0ahUKEwjVsfGcv6PNAhVCfpAKHVGFDHcQ6AEIJjAC#v=onepage&q=pong%20chu%20gcd&f=false), fiquei muito interessado na implementação proposta pelo autor devido a simplicidade, achei uma outra do pessoal do MIT: ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-884-complex-digital-systems-spring-2005/lecture-notes/l02_verilog.pdf, mas achei muito "hardcore" para o pouco tempo que disponho para implementar, avaliar e "colocar funcionando" no laboratório na próxima quarta-feira.

Testei o testbench com as alterações propostas, realmente a questão de testar o circuito de forma síncrona é o esperado, entretanto, o resultado só sai o esperado quando utilizo as cláusulas de espera ("wait"), como exibido abaixo:

Código: Selecionar todos
module gcd_tb();
  reg  clk, rst, start;
  reg  [9:0]i,j;
  wire [8:0]y;
  wire done_tick, ready;

  // Instantiate the Device Under Test (DUT):
  gcd_engine gcd_engine_dut0(clk, rst, start, i, j, done_tick, ready, y);

  // Clock:
  always #10 clk = ~clk;

  // Initial sequential block simulation:
  initial begin
    rst = 1'b1;
    clk = 1'b0;
    #100 rst = 1'b0;
  end

  always @(posedge clk) begin
    if(rst) begin
      i = 1;
      j = 1;     
    end else begin
      wait(ready == 1'b1);
        #5 start = 1'b0;
        j = j + 1;
        if(j > 512) begin
          i = i + 1;
          j = 1;     
          if(i > 512) begin
            i = 1;
            $finish;
          end
        end
        #5 start = 1'b1;
      wait(done_tick == 1'b1);
        $display("%d %d %d", i, j, y);
    end
  end

  initial begin
    $dumpfile("gcd_tb.vcd");
    $dumpvars;
  end
endmodule


O ideal mesmo seria a possibilidade de implementar uma espécie de circuito "combinacional" em que a alteração das entradas levasse a uma nova saída ao término do cálculo, sem a necessidade de um novo "disparo" feito pela entrada "start".
Seu Madruga: "O trabalho não é ruim, ruim é ter que trabalhar"
Avatar do usuário
chrdcv
Dword
 
Mensagens: 1580
Registrado em: 13 Out 2006 14:13

Re: Ajuda testbench Verilog

Mensagempor msamsoniuk » 13 Jun 2016 21:24

pois eh, eu comecaria do conceito mais simples, tipo aquela versao do PC mesmo... e como tem um while ali, significa que o codigo requer multiplos clocks, portanto me parece justo ter um start/done. eu na realidade costumo escrever isso como req/ack: o master envia um req e aguarda o slave terminar e enviar um ack para ir para o proximo ciclo. talvez um proximo passo fosse fazer um pipeline, com req/ack sobrepostos, isso se os modulos de saida forem independentes da entrada... mas daih eh meio detalhe demais. bom, baseado naquele codigo do PC, eu montei um codigo equivalente em verilog, tanto para o gcd quanto para teste dele... para o gcd ficou:

Código: Selecionar todos
module gcd(clk,req,a,b,r,ack);

    input wire clk;
    input wire req;
    input wire [8:0] a;
    input wire [8:0] b;
    output reg [8:0] r;
    output wire ack;

    reg ackff;

    reg [8:0] aff;
    reg [8:0] bff;

    always@(posedge clk)
    begin
        if(req)
        begin
            ackff <= 0;
            aff <= a;
            bff <= b;
        end
        else
        begin
            if(aff != bff)
            begin
                if(aff>bff)
                    aff <= aff-bff;
                else
                    bff <= bff-aff;           
            end
            else
            begin
                r <= aff;
                ackff <= 1;
            end
        end
    end
   
    assign ack = ackff && !req;
   
endmodule


tentei fazer como no PC... a logica eh simples: vc ativa req por um ciclo para copiar os valores iniciais e entao ele roda um loop ateh encontrar a condicao (aff==bff) e ativa o ack. tal como o codigo que vc mostrou ali em C. nao tem muito truque aih nao, eu nao pensei em como otimizar isso tambem... enfim, o importante eh comecar simples, entao montar o teste e a partir do teste comecar a melhorar o modulo principal... o importante, de verdade, eh o teste:

Código: Selecionar todos
module gcd_test;

    reg clk = 0;
    reg res = 1;
    reg req = 0;
   
    reg [8:0] a;
    reg [8:0] b;
   
    wire ack;
    wire [8:0] r;
   
    always #10 clk = !clk;
    always #100 res = 0;

    integer ax,bx,cx,er;

    always@(posedge clk)
    begin
        if(res)
        begin
            req <= 1;
            a <= 1;
            b <= 1;
            cx = 0;
            er = 0;
        end
        else
        if(req)
        begin
            req <= 0;
        end
        else
        if(ack)
        begin
            ax = a;
            bx = b;
            while(ax!=bx)
            begin
                if(ax > bx) ax = ax-bx;
                else bx = bx-ax;
            end
           
            if(r!=ax)
                er = er+1;
            cx = cx+1;
       
            $display("%d %d %d %d (%d expected: %s)",cx,a,b,r,ax,r==ax?"pass":"fail" );       
            req <= 1;
            if(a==10)
            begin
                b <= b+1;
                a <= 1;
               
                if(b==10)
                begin
                    $display("total %d\n pass %d\n fail %d",cx,cx-er,er);
                    $stop;
                end
            end
            else
            begin
                a <= a+1;
            end
        end
    end

    gcd gcd(clk,req,a,b,r,ack);

endmodule


nada de novo, mas veja que nao faco um initial nem nada. quando eu faco, eh para colocar um for() e contar os clocks hehehe mas nesse caso eu usei o rst para dar o pulo inicial no req (meu req eh ativo em nivel 1). com req ativo, o gcd() registra os valores de entrada e eu desativo o req para ele processar, esperando pelo ack. quando ack chega, eu testo o resultado e entao ativo o req para o proximo ciclo. o pulo do gato eh justamente o teste!

no caso, veja que, apesar de verilog ser bem inferior como linguagem em relacao ao C e systemverilog, ainda dah para rodar algum codigo C no meio:

Código: Selecionar todos
            ax = a;
            bx = b;
            while(ax!=bx)
            begin
                if(ax > bx) ax = ax-bx;
                else bx = bx-ax;
            end
           
            if(r!=ax)
                er = er+1;
            cx = cx+1;


isso permite processar ax, bx em um while() como no codigo C que vc mostrou. entao eu incremento cx (contagem total) e comparo ax com r, que eh o resultado do modulo gcd(), incrementando er se houver erro, automatizando o teste! mas talvez fosse uma boa fazer um dump do resultado e comparar com o mesmo codigo rodando no PC, para garantir.

o resultado fica assim:

Código: Selecionar todos
This is a Lite version of ISE Simulator(ISim).
Simulator is doing circuit initialization process.
Finished circuit initialization process.
          1   1   1   1 (          1 expected: pass)
          2   2   1   1 (          1 expected: pass)
          3   3   1   1 (          1 expected: pass)
...
         97   7  10   1 (          1 expected: pass)
         98   8  10   2 (          2 expected: pass)
         99   9  10   1 (          1 expected: pass)
        100  10  10  10 (         10 expected: pass)
total         100
 pass         100
 fail           0
Stopped at time : 13.330 us : File "Z:/Users/marcelo/Documents/Verilog/cache/asm51.v" Line 82
Stopped at line=82 file name=Z:/Users/marcelo/Documents/Verilog/cache/asm51.v
%


como vc pode ver, basta olhar o final para ver que ele nao falhou nenhuma vez! \o/

as formas de onda tambem pareceram bem bacanas:

Imagem
https://darklife.org/marcelo/public/xil ... .18.55.png

olhando o resultado, percebi que nao inicializei os registros em gcd(), mas nao fez muito impacto no resultado. e bom, tendo um teste que funciona, dah para pensar em comecar a otimizar o codigo... mas daih eh assunto para um futuro distante! hehehe
Avatar do usuário
msamsoniuk
Dword
 
Mensagens: 2935
Registrado em: 13 Out 2006 18:04


Voltar para Verilog, VHDL, SystemC ( PLAs, CPLDs, FPGAs, etc... )

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

x