Skip to content

VUnit uputstvo

Dejana Smiljanic edited this page Feb 17, 2026 · 3 revisions

VUnit: Uputstvo

VUnit je open-source framework za unit testiranje VHDL/Verilog koda. Donosi moderne metodologije softverskog testiranja u verifikaciju hardverskog dizajna.

Ključne karakteristike

  • Python-baziran test runner: Koristi Python skripte za otkrivanje, kompajliranje i pokretanje testova
  • Automatsko otkrivanje testova: Automatski pronalazi sve testbench-eve
  • CI/CD prilagođen: Jednostavna integracija u continuous integration pipeline-ove
  • Agnostičan prema simulatoru: Radi sa više simulatora (ModelSim, GHDL)
  • Bogata biblioteka asercija: Pruža sveobuhvatne funkcije za provjeru i logovanje
  • Fleksibilna organizacija testova: Podržava test suite-ove, test case-ove i konfiguracije

Instalacija i podešavanje

Potrebni:

  • Python 3.7 ili noviji (VUnit zahtijeva Python 3.7+)
  • GHDL

Linux instalacija

Debian/Ubuntu

# Instalirajte Python (ako već nije instaliran)
sudo apt update
sudo apt install python3 python3-pip

# Instalirajte GHDL
sudo apt install ghdl

# Instalirajte VUnit
pip3 install vunit_hdl

Arch/Arch-bazirane distribucije

# Instalirajte Python (ako već nije instaliran)
sudo pacman -Syu python

# Instalirajte GHDL
sudo pacman -Syu ghdl

# Instalirajte VUnit iz AUR-a
# Opcija 1 - Korištenjem AUR helper-a:
yay -Syu python-vunit_hdl

# Opcija 2 - Ručna instalacija:
git clone https://aur.archlinux.org/python-vunit_hdl.git
cd python-vunit_hdl
makepkg -si

Windows instalacija

  1. Instalirajte Python: Preuzmite sa python.org i uvjerite se da je dodan u PATH
  2. Instalirajte GHDL: Preuzmite najnovije izdanje sa github.com/ghdl/ghdl/releases, ekstraktujte ga i dodajte bin direktorij u PATH
  3. Instalirajte VUnit: Otvorite Command Prompt i izvršite:
    pip3 install vunit_hdl
    

Upotreba

Pisanje VUnit testbench-eva

Svaki VUnit testbench slijedi ovu strukturu:

library vunit_lib;
context vunit_lib.vunit_context;

entity tb_example is
  generic (runner_cfg : string);
end entity;

architecture tb of tb_example is
begin
  main : process
  begin
    test_runner_setup(runner, runner_cfg);

    while test_suite loop

      if run("test_pass") then
        report "This will pass";

      elsif run("test_fail") then
        assert false report "It fails";

      end if;
    end loop;

    test_runner_cleanup(runner);
  end process;
end architecture;

Ključne komponente

  1. VUnit biblioteka i kontekst: Moraju biti uključeni na vrhu

    library vunit_lib;
    context vunit_lib.vunit_context;
  2. Runner configuration generic: Potreban da bi VUnit kontrolisao testbench

    generic (runner_cfg : string);
  3. Test runner wrapper: Okružuje sav test kod

    test_runner_setup(runner, runner_cfg);
    -- Naš kod za testiranje izmedju
    test_runner_cleanup(runner);

    Važno: Ne koristite wait; prije cleanup linije, jer testbench mora dostići cleanup da bi se pravilno završio.

  4. Test suite petlja: Sadrži sve test case-ove

    while test_suite loop
      if run("test_name_1") then
        -- Test kod
      elsif run("test_name_2") then
        -- Test kod
      end if;
    end loop;

DUT instanciranje

Da bi se komponente definisane u dizajnu uspješno instancirale, potrebno je koristiti definisanu design_lib biblioteku umjesto work biblioteke. Kroz ovu biblioteku pristupa se RTL dizajnu u /hardware/design.

Instanciranju komponente sada treba pristupiti na sljedeći način:

  1. Deklarisati pomenutu biblioteku u okviru testa
library design_lib;
  1. Instancirati komponentu na odgovarajući način u okviru arhitekture testa
uut_entity_name : entity design_lib.entity_name
    port map (
      a_i  => a_i,
      b_i  => b_i,
      c_o  => c_out
    );

pri čemu treba voditi računa da portovi moraju odgovarati onima definisanim u dizajnu i definisanim signalima u okviru arhitekture testa, redom.

Kako funkcioniše izvršavanje testova

VUnit pokreće testbench više puta—jednom za svaki test case. Na primjer, ako imate tri run() komande, testbench će se izvršiti tri puta:

  • Prvo pokretanje: Izvršava se samo kod unutar run("test_1")
  • Drugo pokretanje: Izvršava se samo kod unutar run("test_2")
  • Treće pokretanje: Izvršava se samo kod unutar run("test_3")

Primjer test suite-a

Recimo da imamo alu sa funkcijom reseta i operacijama sabiranja i oduzimanja. Njegov test-suite loop će biti:

while test_suite loop

  if run("test_reset") then
    info("Testing reset functionality");
    rst <= '1';
    wait for clk_period * 2;
    rst <= '0';
    wait for clk_period;
    check_equal(result, std_logic_vector(to_unsigned(0, 8)), 
                "Result should be 0 after reset");

  elsif run("test_add") then
    info("Testing add operation");
    a <= std_logic_vector(to_unsigned(10, WIDTH));
    b <= std_logic_vector(to_unsigned(3, WIDTH));
    op <= "001";
    wait for clk_period;
    check_equal(result, std_logic_vector(to_unsigned(13, WIDTH)));

  elsif run("test_subtract") then
    info("Testing subtract operation");
    a <= std_logic_vector(to_unsigned(10, WIDTH));
    b <= std_logic_vector(to_unsigned(3, WIDTH));
    op <= "010";
    wait for clk_period;
    check_equal(result, std_logic_vector(to_unsigned(7, WIDTH)));

  end if;
end loop;

Grupisanje srodnih testova

Ne moramo imati zasebanu run() komandu za svaki pojedinačni test case. Možemo grupisati srodne testove zajedno:

if run("test_add_exhaustive") then
  info("ADD: Testing all 65,536 combinations");
  
  fail_count := 0;
  test_count := 0;
  op <= "001";  -- ADD operacija
  
  for i in 0 to 255 loop
    for j in 0 to 255 loop
      a <= std_logic_vector(to_unsigned(i, 8));
      b <= std_logic_vector(to_unsigned(j, 8));
      wait for 10 ns;
      
      test_count := test_count + 1;
      
      -- Provjera očekivanog rezultata
      if to_integer(unsigned(result)) /= (i + j) mod 256 then
        fail_count := fail_count + 1;
        error("ADD fail: " & to_string(i) & " + " & to_string(j));
      end if;
      
    end loop;
  end loop;
  
  info("ADD Results:");
  info("  Total: " & to_string(test_count));
  info("  Passed: " & to_string(test_count - fail_count));
  info("  Failed: " & to_string(fail_count));
  
  check(fail_count = 0, "ADD exhaustive test had failures");

elsif run("test_subtract_exhaustive") then
  -- Slično iscrpno testiranje za oduzimanje
  
end if;

VUnit asercije i funkcije za logovanje

Funkcije za logovanje

  • info("poruka") - Ispisuje informativnu poruku na terminal
  • warning("poruka") - Prijavljivanje upozorenja (ne zaustavlja test)
  • error("poruka") - Prijavljuje grešku i zaustavlja trenutni test
  • failure("poruka") - Prijavljuje kritičan neuspjeh i zaustavlja sve testove

Funkcije za asercije

  • check_equal(actual, expected) - Provjerava da li su dvije vrijednosti jednake
  • check_equal(actual, expected, "poruka") - dodatni argument "poruka" koji javlja poruku na neuspiješan check.
  • check(uslov, "poruka") - Provjerava da li je uslov istinit
  • check_true(uslov) - Provjerava da li je uslov istinit
  • check_false(uslov) - Provjerava da li je uslov neistinit, takođe mogu sa porukom.

Primjeri upotrebe

-- Jednostavna provjera jednakosti
check_equal(result, expected_value);

-- Sa opisnom porukom
check_equal(counter_out, std_logic_vector(to_unsigned(5, 8)), 
            "Counter should be 5 after 5 clock cycles");

-- Provjera boolean uslova
check(unsigned(result) < 256, "Result should be less than 256");

-- True/False provjere
check_true(overflow_flag = '1', "Overflow flag should be set");
check_false(underflow_flag = '1', "Underflow flag should not be set");

Pokretanje testova

Da pokrenete sve testove, izvršite sljedeću komandu iz ```/scripts/`` direktorijuma:

python3 run.py

Ovo će otkriti i pokrenuti sve testove u hardware/tests/ direktoriju.

Dodatne komande

# Izlistajte sve dostupne testove
python3 run.py --list

# Primjer ispisa:
# lib.counter_tb.test_reset
# lib.counter_tb.test_count_up
# lib.counter_tb.test_overflow

# Pokrenite specifičan test
python3 run.py "*test_reset"

# Pokrenite sve testove koji odgovaraju obrascu
python3 run.py "*count*"

Najbolje prakse

  1. Organizacija testova: Generisati srodne test case-ove pod smislenim run() imenima
  2. Smislena imena testova: Koristiti jasna, deskriptivna imena za test case-ove
  3. Informativne poruke: Uvijek pružaiti korisne poruke o greškama u asercijama
  4. Nezavisnost testova: Svaki test case treba biti nezavisan i ne treba se oslanjati na druge
  5. Granični slučajevi: Testirajti granične uslove i edge slučajeve
  6. Dokumentacija: Koristiti info() za dokumentovanje šta svaki test radi

Za više informacija, posjetite VUnit dokumentaciju.