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

B膮d藕 aktywny - zaloguj si臋 lub utw贸rz konto!

Tylko zarejestrowani u偶ytkownicy mog膮 komentowa膰 zawarto艣膰 tej strony

Utw贸rz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj si臋 禄

Zaloguj si臋

Posiadasz w艂asne konto? U偶yj go!

Zaloguj si臋 禄
×
×
  • 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.