Skocz do zawartości

Verilog - testowanie modulow


mozerpol

Pomocna odpowiedź

Hej

To mój pierwszy post, wiec cześć wszystkim 🙂

Pytanie dotyczy języka verilog, którego niedawno zacząłem poznawać.

W jednym pliku mam kilka modułów i chciałbym je kolejno przetestować. Poniżej jest owy plik:

module nand_as_not(
  input wire a,
  output wire y
);

  nand(y, a, a);

endmodule

module nor_as_not(
  input wire a,
  output wire y
);

  nor(y, a, a);

endmodule

module nand_as_or(
  input wire a,
  input wire b,
  output y
);
  
  wire first_out, second_out;
  
  nand(first_out, a, a);
  nand(second_out, b, b);
  nand(y, first_out, second_out);
  
endmodule

Testbench dla jednego modułu jest prosty - pobudzamy jego wejścia i odczytujemy wyjścia:

module gates_tb();

  reg a_tb = 1;
  wire y_tb;
  
  nand_as_not UUT1(
    .a(a_tb),
    .y(y_tb)
  );
  
  integer i = 0;
  
  initial begin
    $dumpfile("tb.vcd"); 
    $dumpvars;
    $display("  nand as not ");
    $display(" |  a  | out |");

    for(i=0; i<=3; i=i+1)
      begin
        #1       
        a_tb = 1^a_tb;
        $monitor(" |  %d  |  %d  |", a_tb, y_tb);
      end
  end
endmodule

Wynik symulatora (używam edaplayground.com) to:

nand as not
| a | out |
| 0 | 1 |
| 1 | 0 |
| 0 | 1 |
| 1 | 0 |

Jednak mam problem kiedy chce kolejno testować moduły. Pytanie, czy w ogóle tak się da. Przyzwyczajony jestem do języków typu C czy Python, gdzie kolejno można wywoływać sobie funkcje, przekazując im argumenty. Tutaj uruchamiając taki kod:

module gates_tb();

  reg a_tb = 1;
  wire y_tb;
  
  nand_as_not UUT1(
    .a(a_tb),
    .y(y_tb)
  );
  
  integer i = 0;
  
  initial begin
    $dumpfile("tb.vcd"); 
    $dumpvars;
    $display("  nand as not ");
    $display(" |  a  | out |");

    for(i=0; i<=3; i=i+1)
      begin
        #1       
        a_tb = 1^a_tb;
        $monitor(" |  %d  |  %d  |", a_tb, y_tb);
      end
  end
 
  nor_as_not UUT2(
    .a(a_tb),
    .y(y_tb)
  );  
  
  initial begin
    $display("   nor as not ");
    $display(" |  a  | out |");

    for(i=0; i<=3; i=i+1)
      begin
        #1       
        a_tb = 1^a_tb;
        $monitor(" |  %d  |  %d  |", a_tb, y_tb);
      end
  end
       
endmodule

Uzyskuje:

nand as not
| a | out |
nor as not
| a | out |
| 1 | 0 |
| 1 | 0 |
| 0 | 1 |

Co jest zrozumiale, bo blok initial wykonuje się w czasie t=0, skoro są dwa to wykonują się równolegle. Wiec jak umieścić w jednym module kilka funkcji testujących pracujących jedna po drugiej?

Link do komentarza
Share on other sites

Cześć,

w językach HDL trochę inaczej się do tego podchodzi. Każdy moduł ma swój interface , czyli porty wejściowe i wyjściowe. Wystarczy cyklicznie za pomocą zegara pobudzać porty wejściowe i w oknie symulatora obserwować interesujące nas sygnały (wejściowe i wyjściowe).  Musisz utworzyć instancje testowe swoich modułów i jako sygnały wejściowe podawać im pobudzenia (wartości generowane w teście). Zaprezentuję przykład na jednym prostym module, ale różnica przy wielu modułach jest taka, że tworzymy instancje wszystkich modułów i cyklicznie (z okresem zegara) generujemy dla nich wartości sygnałów wejściowych. Oto przykład  - prosty moduł stopera w Verilogu (moduł stopwatch):

`timescale 1ns / 10ps

module stopwatch(
    input clock,
    input reset,
    input start,
    output a, b, c, d, e, f, g, dp,
    output [3:0] an,
    output [3:0] d0,
    output [3:0] d1,
    output [3:0] d2,
    output [3:0] d3
);
 
    
                                                // 4 zmienne przechowujące 1 cyfrę :
    reg [3:0] reg_d0,                           // częsci dziesiętne sekundy
                reg_d1,                         // sekundy
                reg_d2,                         // sekundy*10
                reg_d3;                         // minuty

    // przepisanie wartości na wyjście na potrzeby testbench
    assign d0 = reg_d0;     
    assign d1 = reg_d1;
    assign d2 = reg_d2;
    assign d3 = reg_d3;
            
    reg [22:0] ticker;                          //zmienna potrzebna do podziały częstotliwości, aby otrzymać zegar 10hz
    wire click;


// always, w którym następuje podział częstotliwości 50Mhz przez 5.000.000 aby otrzymać 10Hz (12 MHz przez 1200000)
    always @ (posedge clock or posedge reset) begin         //ten always wywołuje się na narastającym zboczu zegara "clock" lub wejścia "reset"
        if(reset)                                           // jeżeli reset = 1
            ticker <= 0;                                    // zerujemy licznik, który służy do dzielenia częstotliwoci
        else if(ticker == 1_199_999)                          // jeżeli licznik == 4.999.999 | 1 199 999
            ticker <= 0;                                    // zerujemy go (liczymy od 0 do 4.999.999, czyli 5mln), tak jak od 0 do 1 to 2, bo (0, 1)
        else if(start)                                      // jeżeli ticker różne od 4.999.999 i start == 1
            ticker <= ticker + 1;                           // dodajemy 1 do zmiennej ticker
    end

    assign click = ((ticker == 1_199_999)?1'b1:1'b0);         // zmienna 'click' = 1 raz co 0.1s (10Hz),

    always @ (posedge clock or posedge reset) begin         // ten always wywołuje się na narastającym zboczu zegara "clock" lub wejścia "reset"
        if (reset) begin                                    // jeżeli reset == 1
            reg_d0 <= 0;                                    // zerujemy wszystkie cyfry
            reg_d1 <= 0;
            reg_d2 <= 0;
            reg_d3 <= 0;
        end
        
        else if (click) begin
            if(reg_d0 == 9) begin                           // jeżeli reg_d0 == 9, znaczy że 
                reg_d0 <= 0;                                    // zerujemy reg_d0 (najmłodsza cyfra)(9 -> 0)
                                                                // teraz będziemy musieli dodać 1 do cyfry sekund
                if (reg_d1 == 9) begin                          // jeżeli ta cyfra sekund to 9
                    reg_d1 <= 0;                                    // to ją zerujemy (9 -> 0)
                                                                    // jeżeli (9 ->0) cyfra sekund, to musimy dodać 1 na 2 pozycji sekund
                    if (reg_d2 == 5) begin                          // jeżeli ta cyfra == 5
                        reg_d2 <= 0;                                    // to zerujemy ją (5 -> 0) bo liczymy sekundy do (59 -> 00)
                                                                        // musimy dodać 1 do minut
                        if(reg_d3 == 9)                                 // jeżeli licznik minut == 9
                            reg_d3 <= 0;                                    // to zerujemy licznik minut
                        else                                            // jeżeli jednak licznik minut różny od 9
                            reg_d3 <= reg_d3 + 1;                           // dodajemy 1 do licznika minut
                    end
                    else //else_3                                   // jeżeli 2 cyfra sekund różna od 5
                        reg_d2 <= reg_d2 + 1;                           // dodajemy do niej 1
                end
                else //else_2                                   // jeżeli 1 cyfra sekund różna od 9
                    reg_d1 <= reg_d1 + 1;                           //dodajemy do niej 1
            end 
                
            else //else_1                                   // jeżeli cyra dziesiętnych sekund różna od 9
                reg_d0 <= reg_d0 + 1;                           // dodajemy do niej 1
        end
    end

    reg [17:0]count;
    
    // dzielnik częstotliwości zrealizowany na liczniku
    
    always @ (posedge clock or posedge reset) begin                 // always wywołuje się na narastającym zboczu zegara (50MHz) lub resetu
        if (reset)                      // jeżeli reset == 1
            count <= 0;                 // zerujemy licznik
        else                            // w pzreciwnym wypadku
            count <= count + 1;         // dodajemy do licznika 1 50mln razy na sekundę
    end
    
    reg [6:0]sseg;
    reg [3:0]an_temp;
    reg reg_dp;
    
    always @ (*) begin
        case(count[17:16])              // sprawdzamy 2 najstarsze bity licznika
        
            2'b00 : begin               // jeżeli równe 00
                sseg = reg_d0;          // konwersji poddajemy najmłodszą cyfrę 
                an_temp = 4'b1110;      // włączamy do wyświetlania najmłodszą cyfrę
                reg_dp = 1'b1;          // wyłączamy kropkę
            end
            
            2'b01 :  begin              // jeżeli równe 01                          
                sseg = reg_d1;          // konwersji poddajemy cyfrę sekund   
                an_temp = 4'b1101;      // włączamy do wyświetlania 2 od prawej strony cyfrę
                reg_dp = 1'b0;          // włączamy kropkę                         
            end
            
            2'b10 : begin               // jeżeli równe 10                          
                sseg = reg_d2;          // konwersji poddajemy cyfrę dziesiątek sekund (10*s)     
                an_temp = 4'b1011;      // włączamy do wyświetlania 3 od prawej strony cyfrę
                reg_dp = 1'b1;          // wyłączamy kropkę                         
            end
            
            2'b11 : begin               // jeżeli równe 11                          
                sseg = reg_d3;          // konwersji poddajemy cyfrę minut
                an_temp = 4'b0111;      // włączamy do wyświetlania 4 od prawej strony cyfrę
                reg_dp = 1'b0;          // włączamy kropkę                         
            end
            
        endcase
    end
    
    
    assign an = an_temp;

// dekodowanie cyfry zawartej w zmiennej "sseg" na taką, aby można było wyświetlić na wyświetlaczy 7-seg
    reg [6:0] sseg_temp; 
    
    always @ (*) begin
        case(sseg)
            4'd0 : sseg_temp = 7'b1000000;
            4'd1 : sseg_temp = 7'b1111001;
            4'd2 : sseg_temp = 7'b0100100;
            4'd3 : sseg_temp = 7'b0110000;
            4'd4 : sseg_temp = 7'b0011001;
            4'd5 : sseg_temp = 7'b0010010;
            4'd6 : sseg_temp = 7'b0000010;
            4'd7 : sseg_temp = 7'b1111000;
            4'd8 : sseg_temp = 7'b0000000;
            4'd9 : sseg_temp = 7'b0010000;
            default : sseg_temp = 7'b0111111; //dash
        endcase
    end
    
    assign {g, f, e, d, c, b, a} = sseg_temp; 
    assign dp = reg_dp;

endmodule

Jak widzimy mnoduł ma tylko trzy porty wejściowe i te musimy w teście pobudzać:

    input clock,
    input reset,
    input start,

A oto test dla tego modułu:

`timescale 1ns / 10ps

////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer:
//
// Create Date:   08:29:26 06/19/2018
// Design Name:   stopwatch
// Module Name:   C:/Users/MGabryelski/Documents/Xilinx/_DEV/Stopwatch_Verilog01/stopwatchTest.v
// Project Name:  Stopwatch_Verilog01
// Target Device:  
// Tool versions:  
// Description: 
//
// Verilog Test Fixture created by ISE for module: stopwatch
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
////////////////////////////////////////////////////////////////////////////////

module stopwatchTest;

	// Inputs
	reg clock;
	reg reset;
	reg start;

	// Outputs
	wire a;
	wire b;
	wire c;
	wire d;
	wire e;
	wire f;
	wire g;
	wire dp;
	wire [3:0] an;
	wire [3:0] d0;
	wire [3:0] d1;
	wire [3:0] d2;
	wire [3:0] d3;

	// Instantiate the Unit Under Test (UUT)
	stopwatch uut (
		.clock(clock), 
		.reset(reset), 
		.start(start), 
		.a(a), 
		.b(b), 
		.c(c), 
		.d(d), 
		.e(e), 
		.f(f), 
		.g(g), 
		.dp(dp), 
		.an(an), 
		.d0(d0), 
		.d1(d1), 
		.d2(d2), 
		.d3(d3)
	);

	 initial begin
        clock = 0;
        forever
            #42 clock = ~clock;
    end

    initial begin       
        // Wait 100 ns for global reset to finish
        #100;
        reset = 1;
        #100;
        reset = 0;
        #100;
        start = 1;
        // Add stimulus here
		  //tutaj dajemy dalsze pobudzenia
    end
     
endmodule

To jest generacja zegara (sygnał: clock):

    initial begin
        clock = 0;
        forever
            #42 clock = ~clock;
    end

, a dalej są generowane pobudzenia dla sygnałów reset i start. Odpalamy symulację i możemy obserwować przebiegi czasowe dla naszych modulów.

Pozdrawiam

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.