Wykład 5: Symulacja, testy, weryfikacja formalna

Data: 07.11.2018

Symulatory Veriloga

Symulator języka Verilog to program bezpośrednio ewaluujący konstrukcje Veriloga (w przeciwieństwie do syntezy, czyli tłumaczenia na sprzęt). Można w nim używać pełnego zbioru funkcjonalności języka Verilog (nie tylko syntezowalnego podzbioru) – symulatory pozwalają na przykład wypisać coś na ekran bądź otwierać pliki na dysku. Symulatorów używa się do przetestowania poprawnego działania układu przed uruchomieniem go na sprzęcie.

Wspomnę tutaj o następujących symulatorach:

  • Icarus Verilog (znany również jako iverilog) – otwarty symulator Veriloga.

  • Verilator – tłumaczy syntezowalny kod Veriloga na kod C++, który można wlinkować do własnego programu. Nie może być używany do uruchamiania zwykłych testów napisanych w Verilogu, za to pozwala nam używać naszego symulowanego układu z poziomu C++. Open source.

  • XSIM – symulator Xilinxa, wbudowany w ISE. Oprócz Veriloga wspiera VHDL i testowanie zamkniętych logic core’ów. Bardzo nie open source.

Icarus Verilog – użycie

a.v:

module hello;

initial begin
        $display("Hello, world!");
end

endmodule

Użycie:

# -s wybiera główny moduł.
$ iverilog a.v -s hello
$ ./a.out
Hello, world!
$

Informacja

Wygenerowany plik a.out nie jest kodem maszynowym, tylko skryptem napisanym w reprezentacji pośredniej Icarus Veriloga).

Testy

Aby symulacja miała sens, musimy napisać przypadki testowe (tzw. testbench). Przypadek testowy to moduł w (niesyntezowalnym) Verilogu, który używa danego syntezowalnego modułu, który chcemy przetestować. Testowany moduł przyjęło się nazywać DUT (Device Under Test):

module flop(
    input wire D,
    input wire CLK,
    output reg Q
);

initial Q = 0;

always @(posedge CLK)
    Q <= D;

endmodule

module testbench;

reg D = 0;
reg CLK = 0;
wire Q;

flop DUT(D, CLK, Q);

initial begin
    if (Q !== 0) $display("Err 1");
    D = 1;
    // #<X> czeka X kroków symulacji.
    #1 if (Q !== 0) $display("Err 2");
    CLK = 1;
    #1 if (Q !== 1) $display("Err 3");
    D = 0;
    CLK = 0;
    #1 if (Q !== 1) $display("Err 4");
    D = 0;
    CLK = 1;
    #1 if (Q !== 0) $display("Err 5");
    $finish;
end

endmodule

(test_flop.v)

Aby dostać się do wewnętrznych sygnałów testowanego modułu, można użyć składni DUT.<nazwa_sygnału> (narusza to enkapsulację, ale w testach można).

Warto tutaj wspomnieć o dwóch typach porównania w Verilogu: == (i jego negacja !=) oraz === (i negacja !==). == jest syntezowalnym porównaniem, w którym stany nieokreślone się propagują (0 == X daje wynik X, X == X daje wynik X) – jest to porównanie, którego należy używać zawsze w syntezowalnym kodzie. === natomiast jest porównaniem, które traktuje każdą z 4 możliwych wartości bitu jako niezależną wartość (0 === X daje wynik 0, X === X daje wynik 1) – nelaży go używać w testach, by upewnić się, że nasz układ daje odpowiednie wyniki.