Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Industrial programming >> VHDL

Memulai dengan VUnit

VUnit adalah salah satu kerangka kerja verifikasi VHDL open-source paling populer yang tersedia saat ini. Ini menggabungkan runner suite pengujian Python dengan pustaka VHDL khusus untuk mengotomatiskan bangku pengujian Anda.

Untuk memberi Anda tutorial VUnit gratis ini, VHDLwhiz meminta Ahmadmunthar Zaklouta, yang berada di balik sisa artikel ini, termasuk proyek contoh VUnit sederhana yang dapat Anda unduh dan jalankan di komputer Anda.

Ayo beri tahu Ahmad!

Tutorial ini bertujuan untuk mendemonstrasikan penggunaan framework VUnit dalam proses verifikasi desain Anda. Ini akan memandu Anda melalui proses pengaturan VUnit, membuat testbench VUnit, menggunakan perpustakaan pemeriksaan VUnit, dan menjalankan tes VUnit di ModelSim. Ini juga menunjukkan beberapa teknik verifikasi.

Ringkasan

Artikel ini berisi beberapa tangkapan layar. Klik gambar untuk memperbesar!

Gunakan bilah sisi untuk menavigasi garis besar untuk tutorial ini, atau gulir ke bawah dan klik tombol navigasi pop-up di pojok kanan atas jika Anda menggunakan perangkat seluler.

Persyaratan

Tutorial ini mengasumsikan perangkat lunak ini diinstal pada mesin Windows:

Ini juga mengasumsikan memiliki pengetahuan dasar VHDL dan keterampilan ModelSim.

Memasang

git clone --recurse-submodules https://github.com/VUnit/vunit.git
 
python setup.py install

Unduh proyek contoh

Anda dapat mengunduh contoh proyek dan kode VHDL menggunakan formulir di bawah ini.

Ekstrak Zip  ke C:\vunit_tutorial .

Pengantar

VUnit adalah kerangka kerja pengujian untuk HDL yang memudahkan proses verifikasi dengan menyediakan alur kerja yang digerakkan oleh pengujian “pengujian lebih awal dan sering” dan kotak peralatan untuk otomatisasi dan administrasi yang menjalankan pengujian. Ini adalah kerangka kerja canggih dengan fitur kaya yang luas, namun mudah digunakan dan beradaptasi. Ini sepenuhnya open-source dan dapat dengan mudah dimasukkan ke dalam metodologi pengujian tradisional.

VUnit terdiri dari dua komponen utama:

Bagian VHDL terdiri dari enam perpustakaan, seperti yang ditunjukkan pada diagram di bawah ini. Tutorial ini akan menggunakan pustaka logging dan pengecekan.

Desain sedang diuji

Desain yang digunakan dalam tutorial ini bernama motor_start , mengimplementasikan prosedur start untuk motor tertentu dan menggerakkan 3 LED yang mewakili status motor.

Antarmuka terdiri dari catatan masukan motor_start_in dengan 3 elemen (3 sakelar sebagai input) dan catatan keluaran motor_start_out dengan 3 elemen (3 LED MERAH, KUNING, HIJAU sebagai output).

Beberapa motor memerlukan inisialisasi di awal sebelum Anda dapat mulai menggunakannya. Prosedur start-up motor kami memiliki tiga langkah:

  1. Memuat konfigurasi
  2. Kalibrasi
  3. Rotasi

Urutan startup

Berikut urutan startup motor dan arti dari indikator LED.

  1. Aktifkan sakelar_1.
  2. RED_LED akan mulai berkedip 5 kali.
  3. Tunggu hingga RED_LED berhenti berkedip dan terus menyala.
  1. Sekarang Anda dapat mengaktifkan switch_2.
  2. Saat sakelar_2 AKTIF, YELLOW_LED akan mulai menyala terus-menerus setelah 5 siklus clock, berlangsung selama 10 siklus clock. Dan setelah itu, YELLOW_LED dan RED_LED akan MATI.
  1. Sekarang, motor siap digunakan. Setiap kali sakelar_3 AKTIF, GREEN_LED akan mulai menyala terus-menerus hingga sakelar_3 OFF.
  2. Pelanggaran apa pun terhadap urutan ini akan mengakibatkan ketiga LED terus menyala hingga semua sakelar dimatikan, dan urutan dapat dimulai lagi.

Pengembangan meja ujian

Bagian dari tutorial ini terdiri dari subbagian berikut:

  1. Menyiapkan skrip menjalankan Python
  2. Menyiapkan kerangka VUnit
  3. Menyiapkan meja ujian

Menyiapkan skrip menjalankan Python run.py

Setiap proyek VUnit membutuhkan skrip python tingkat atas run.py yang bertindak sebagai titik masuk ke proyek, dan menentukan semua desain VHDL, testbench, dan sumber perpustakaan.

File ini biasanya ada di direktori proyek. Pohon direktori yang digunakan dalam tutorial ini adalah sebagai berikut:

Dalam run.py file, kita perlu melakukan tiga hal:

1 – Ambil jalur tempat file ini berada dan tentukan jalur untuk file desain dan testbench.

# ROOT
ROOT = Path(__file__).resolve().parent
# Sources path for DUT
DUT_PATH = ROOT / "design"
# Sources path for TB
TEST_PATH = ROOT / "testbench"

2 – Buat instance VUnit.
Ini akan membuat instance VUnit dan menetapkannya ke variabel bernama VU . Kemudian kita dapat menggunakan VU untuk membuat perpustakaan dan berbagai tugas.

# create VUnit instance
VU = VUnit.from_argv()
VU.enable_location_preprocessing()

3 – Buat perpustakaan dan tambahkan sumber VHDL ke dalamnya.
Saya suka memisahkan bagian desain dari bagian testbench. Oleh karena itu, kami akan membuat dua perpustakaan:design_lib dan tb_lib .

# create design library
design_lib = VU.add_library("design_lib")
# add design source files to design_lib
design_lib.add_source_files([DUT_PATH / "*.vhdl"])

# create testbench library
tb_lib = VU.add_library("tb_lib")
# add testbench source files to tb_lib
tb_lib.add_source_files([TEST_PATH / "*.vhdl"])

Sisa file adalah konfigurasi untuk ModelSim untuk menggunakan wave.do file jika ada.

Catatan: di sini, saya menggunakan *.vhdl perpanjangan. Anda mungkin perlu memodifikasinya jika menggunakan *.vhd .

Jika Anda menyukai struktur kerja ini, maka Anda tidak perlu mengubah file ini sama sekali. Setiap kali Anda memulai proyek baru, salin saja ke direktori proyek Anda. Namun, jika Anda lebih suka struktur direktori yang berbeda, Anda perlu memodifikasi jalur agar sesuai dengan struktur kerja Anda.

Sekarang, setiap kali kami menggunakan file ini, VUnit akan secara otomatis memindai bangku uji VUnit di proyek Anda, menentukan urutan kompilasi, membuat perpustakaan dan mengkompilasi sumber ke dalamnya, dan secara opsional menjalankan simulator dengan semua atau kasus pengujian tertentu.

Bukankah itu luar biasa?

Menyiapkan kerangka VUnit

Untuk membuat testbench VUnit, kita perlu menambahkan beberapa kode khusus ke file testbench kita motor_start_tb , seperti yang dijelaskan dalam subbagian ini.

1 – Tambahkan perpustakaan sebagai berikut.

Pertama, kita perlu menambahkan pustaka VUnit VUNIT_LIB dan konteksnya:VUNIT_CONTEXT , sehingga kita memiliki akses ke fungsionalitas VUnit sebagai berikut:

LIBRARY VUNIT_LIB;
CONTEXT VUNIT_LIB.VUNIT_CONTEXT;

Kedua, kita perlu menambahkan perpustakaan desain dan testbench DESIGN_LIB dan TB_LIB sehingga kami memiliki akses ke DUT dan paket kami sebagai berikut:

LIBRARY DESIGN_LIB;
USE DESIGN_LIB.MOTOR_PKG.ALL;

LIBRARY TB_LIB;
USE TB_LIB.MOTOR_TB_PKG.ALL;

DUT memiliki dua paket; satu untuk desain:motor_pkg dan yang lainnya untuk elemen testbench motor_tb_pkg . Itu adalah paket sepele yang saya buat karena biasanya ini adalah bagaimana proyek besar terstruktur. Saya ingin menunjukkan bagaimana VUnit menanganinya.

2 – Tambahkan konfigurasi runner ke entitas sebagai berikut:

ENTITY motor_start_tb IS

  GENERIC(runner_cfg : string := runner_cfg_default);

END ENTITY motor_start_tb;

runner_cfg adalah konstanta generik yang memungkinkan test runner python mengontrol testbench. Artinya, kita dapat menjalankan tes dari lingkungan python. Generik ini wajib dan tidak berubah.

3 – Tambahkan kerangka testbench VUnit ke testbench kami sebagai berikut:

ARCHITECTURE tb OF motor_start_tb IS
  test_runner : PROCESS
  BEGIN
    -- setup VUnit
    test_runner_setup(runner, runner_cfg);

    test_cases_loop : WHILE test_suite LOOP
    
      -- your testbench test cases here
    
    END LOOP test_cases_loop;
    
    test_runner_cleanup(runner); -- end of simulation
  END PROCESS test_runner;
  
END ARCHITECTURE tb;

test_runner adalah proses pengendalian utama untuk testbench. Itu selalu dimulai dengan prosedur test_runner_setup dan diakhiri dengan prosedur test_runner_cleanup . Simulasi hidup di antara dua prosedur ini. test_cases_loop adalah setelan pengujian kami di mana semua kasus pengujian kami berlangsung.

Untuk membuat kasus uji, kami menggunakan run VUnit fungsi dalam pernyataan If sebagai berikut:

IF run("test_case_name") THEN
  -- test case code here

ELSIF run("test_case_name") THEN
  -- test case code here

END IF;

Kemudian kita dapat menjalankan semua atau kasus uji tertentu dari lingkungan Python hanya dengan memanggilnya dengan nama yang kita tentukan dalam panggilan ke run .

Menyiapkan testbench

Di sini, kita mulai dengan menambahkan sinyal yang diperlukan untuk berkomunikasi dengan DUT, seperti yang ditunjukkan di bawah ini:

--------------------------------------------------------------------------
-- TYPES, RECORDS, INTERNAL SIGNALS, FSM, CONSTANTS DECLARATION.
--------------------------------------------------------------------------
-- CONSTANTS DECLARATION --
-- simulation constants
CONSTANT C_CLK_PERIOD : time := 10 ns;

-- INTERNAL SIGNALS DECLARATION --
-- DUT interface
SIGNAL clk             : STD_LOGIC := '0';
SIGNAL reset           : STD_LOGIC := '1';
SIGNAL motor_start_in  : MOTOR_START_IN_RECORD_TYPE := 
  (switch_1 => '0', switch_2 => '0', switch_3 => '0');

SIGNAL motor_start_out : MOTOR_START_OUT_RECORD_TYPE;

-- simulation signals
SIGNAL led_out : STD_LOGIC_VECTOR(2 DOWNTO 0) := (OTHERS => '0');

Catatan: Ini adalah praktik yang baik untuk menginisialisasi sinyal dengan nilai awal.

Selanjutnya, buat instance DUT seperti ini:

--------------------------------------------------------------------------
-- DUT INSTANTIATION.
--------------------------------------------------------------------------
motor_start_tb_inst : ENTITY DESIGN_LIB.motor_start(rtl)
  PORT MAP( 
    clk             => clk, 
    reset           => reset,
    motor_start_in  => motor_start_in,
    motor_start_out => motor_start_out
  ); 

Catatan: Saya mengelompokkan port input dan port output dalam catatan. Saya menemukan ini bermanfaat dalam proyek-proyek besar karena membuat entitas dan instantiasi tidak terlalu berantakan.

Dan terakhir, drive clk , reset , dan led_out seperti yang ditunjukkan di sini:

--------------------------------------------------------------------------
-- SIGNAL DEFINITION OF UNUSED OUTPUT PORTS AND FIXED SIGNALS.
--------------------------------------------------------------------------
led_out(0) <= motor_start_out.red_led;
led_out(1) <= motor_start_out.yellow_led; 
led_out(2) <= motor_start_out.green_led; 

--------------------------------------------------------------------------
-- CLOCK AND RESET.
--------------------------------------------------------------------------
clk   <= NOT clk after C_CLK_PERIOD / 2;
reset <= '0' after 5 * (C_CLK_PERIOD / 2);

Pengembangan kasus uji

Sekarang mari kita kembali ke DUT kita dan memulai pekerjaan sebenarnya dengan mengembangkan beberapa kasus uji. Saya akan menyajikan dua skenario:

Skenario insinyur desain: dari sudut pandang desainer, desainer sendiri yang melakukan verifikasi. Dalam skenario ini, yang biasanya terjadi selama fase pengembangan, perancang dapat mengakses kode. Skenario ini akan menunjukkan bagaimana VUnit membantu kita “menguji lebih awal dan sering”.

Skenario insinyur verifikasi :desain (DUT) diperlakukan sebagai kotak hitam. Kami hanya mengetahui antarmuka eksternal dan persyaratan pengujian.

Kami juga akan melihat tiga teknik verifikasi ini:

Mari kita mulai dengan teknik pertama dan kembali ke dua metode terakhir nanti di artikel ini.

Driver dan pemeriksa dalam kasus uji

Ini adalah pendekatan yang paling mudah. Pengemudi dan pemeriksa berada di dalam kotak uji itu sendiri, kami menerapkan operasi mengemudi dan memeriksa dalam kode kasus uji.

Mari kita asumsikan bahwa kita mengembangkan fungsionalitas RED_LED seperti di bawah ini:

WHEN SWITCH_1_ON =>
  IF (motor_start_in.switch_1 = '0' OR
      motor_start_in.switch_2 = '1' OR
      motor_start_in.switch_3 = '1') THEN
    state = WAIT_FOR_SWITCHES_OFF;
  ELSIF (counter = 0) THEN
    led_s.red_led <= '1';
    state         <= WAIT_FOR_SWITCH_2;
  ELSE
    led_s.red_led <= NOT led_s.red_led;
  END IF;

Dan sekarang, kami ingin memverifikasi desain kami sebelum melanjutkan untuk mengembangkan fungsionalitas lainnya.

Untuk melakukannya, kami menggunakan run VUnit fungsi di dalam test_suite untuk membuat kasus uji untuk memverifikasi output dari menghidupkan sakelar_1, seperti yang ditunjukkan di bawah ini:

IF run("switch_1_on_output_check") THEN
  info("------------------------------------------------------------------");
  info("TEST CASE: switches_off_output_check");
  info("------------------------------------------------------------------");
  -- code for your test case here.

Ini akan membuat kasus uji bernama “switch_1_on_output_check”

Catatan: info adalah prosedur VUnit dari perpustakaan logging yang mencetak ke layar transkrip dan terminal. Kami akan menggunakannya untuk menampilkan hasil tes.

Sekarang, kita akan menulis kode untuk test case ini. Kami akan menggunakan subprogram pemeriksaan VUnit untuk melakukannya.

Kita tahu bahwa RED_LED akan berkedip 5 kali setelah switch_1 AKTIF, jadi kita membuat loop VHDL For dan melakukan pengecekan di dalamnya.

check prosedur melakukan pemeriksaan pada parameter tertentu yang kami berikan. Ini memiliki banyak varian, dan di sini, saya menggunakan beberapa di antaranya untuk tujuan demonstrasi.

check(expr => motor_start_out.red_led = '1', 
      msg  => "Expect red_led to be ON '1'");

Di sini, ia akan menguji apakah RED_LED adalah '1' pada titik waktu simulasi ini dan mencetak pesan ke konsol:

# 35001 ps - check - PASS - red_led when switch_1 on (motor_start_tb.vhdl:192)

Catatan yang memberi tahu kami apakah itu PASS atau ERROR, dan stempel waktu saat pemeriksaan ini terjadi, bersama dengan nama file dan nomor baris tempat pemeriksaan ini.

Cara lain adalah dengan menggunakan check_false prosedur. Di sini, kami menggunakannya untuk memeriksa apakah YELLOW_LED adalah '0':

check_false(expr => ??motor_start_out.yellow_led, 
            msg  => result("for yellow_led when switch_1 on"));

Di sini, kami menggunakan result VUnit berfungsi untuk menyempurnakan pesan. Hasil cetaknya akan terlihat seperti ini:

# 35001 ps - check - PASS - False check passed for yellow_led when switch_1 on 
#                           (motor_start_tb.vhdl:193)

Catatan yang mencetak informasi tambahan tentang jenis cek:“Cek salah lulus”.

Cara lain adalah dengan menggunakan check_equal . Di sini, kami menggunakannya untuk menguji bahwa GREEN_LED adalah '0':

check_equal(got      => motor_start_out.green_led, 
            expected => '0',
            msg      => result("for green_led when switch_1 on"));

Di sini, kami menyediakan parameter tambahan '0' untuk perbandingan. Hasil cetakannya adalah:

# 35001 ps - check - PASS - Equality check passed for green_led when switch_1 on - 
#                           Got 0. (motor_start_tb.vhdl:194)

Sekarang, kita tahu bahwa setelah satu siklus clock, RED_LED akan OFF, dan LED lainnya juga akan mati. Kita dapat menggunakan check_equal untuk memeriksa semuanya secara bersamaan, seperti yang ditunjukkan di bawah ini:

check_equal(led_out, STD_LOGIC_VECTOR'("000"), 
            result("for led_out when switch_1 on"), warning);

Catatan penggunaan kualifikasi STD_LOGIC_VECTOR'("000") , sehingga nilainya tidak ambigu untuk prosedur. Selain itu, kami menetapkan level pemeriksaan ini menjadi PERINGATAN, artinya jika pemeriksaan ini gagal, pemeriksaan ini akan mengeluarkan peringatan alih-alih melontarkan kesalahan. Outputnya akan terlihat seperti ini:

#  45001 ps - check - PASS - Equality check passed for led_out when switch_1 on - 
#                            Got 000 (0). (motor_start_tb.vhdl:197)

Ini adalah kode untuk kasus uji lengkap:

WAIT UNTIL reset <= '0';
WAIT UNTIL falling_edge(clk);
motor_start_in.switch_1 <= '1';  -- turn on switch_1
FOR i IN 0 TO 4 LOOP
  WAIT UNTIL rising_edge(clk);
  WAIT FOR 1 ps;
  check(expr => motor_start_out.red_led = '1', 
        msg  => "Expect red_led to be ON '1'");

 check_false(expr => ??motor_start_out.yellow_led, 
             msg  => result("for yellow_led when switch_1 on"));

 check_equal(got      => motor_start_out.green_led, 
             expected => '0',
             msg      => result("for green_led when switch_1 on"));   

  WAIT UNTIL rising_edge(clk);
  WAIT FOR 1 ps;
  check_equal(led_out, STD_LOGIC_VECTOR'("000"), 
              result("for led_out when switch_1 on"), warning);
END LOOP;
info("===== TEST CASE FINISHED =====");

Uji kasus berjalan

Sekarang saatnya untuk menjalankan test case kami. Kita dapat menjalankan tes di terminal atau di simulator GUI.

Menjalankan tes di terminal

Untuk melakukannya, mulailah dengan membuka terminal baru (CMD, PowerShell, Terminal Windows) dan navigasikan ke vunit_tutorial direktori tempat run.py file berada.

Untuk menjalankan test case, cukup ketik:

python .\run.py *switch_1_on_output_check

VUnit akan mengkompilasi semua file VHDL dalam urutan yang benar dan menguraikannya, mencari testbenches VUnit. Dan kemudian, VUnit akan melihat ke dalam file-file itu, mencari run fungsi dengan nama kasus uji “switch_1_on_output_check” untuk menjalankannya.

Catatan: kami menempatkan simbol karakter pengganti * sebelum kasus uji agar sesuai dengan nama lengkapnya, yaitu:

tb_lib.motor_start_tb.switch_1_on_output_check

Kita bisa melakukannya karena antarmuka baris perintah VUnit menerima wildcard.

Hasil printout setelah simulasi adalah:

Starting tb_lib.motor_start_tb.switch_1_on_output_check
Output file: C:\vunit_tutorial\vunit_out\test_output\tb_lib.motor_start_tb.
switch_1_on_output_check_6df3cd7bf77a9a304e02d3e25d028a92fc541cf5\output.txt
pass (P=1 S=0 F=0 T=1) tb_lib.motor_start_tb.switch_1_on_output_check (1.1 seconds)

==== Summary ==========================================================
pass tb_lib.motor_start_tb.switch_1_on_output_check (1.1 seconds)
=======================================================================
pass 1 of 1
=======================================================================
Total time was 1.1 seconds
Elapsed time was 1.1 seconds
=======================================================================
All passed!

Kita dapat melihat bahwa satu tes telah berjalan, dan itu adalah LULUS.

Catatan bahwa VUnit telah membuat vunit_out folder di direktori proyek. Di dalam folder ini, ada folder bernama test_output yang memiliki laporan tentang pengujian.

Di atas, kami hanya mendapatkan hasil tes akhir tanpa detail tentang setiap pemeriksaan, tetapi alat baris perintah VUnit menyediakan beberapa sakelar untuk menjalankan tes. Untuk mendapatkan informasi lebih lanjut tentang apa yang terjadi selama simulasi, kita dapat menggunakan tombol verbose -v :

python .\run.py *switch_1_on_output_check -v

Hasil cetak verbose akan terlihat seperti ini:

Sakelar berguna lainnya adalah:

-l, --list :Daftar semua kasus uji.

--clean :Hapus folder keluaran terlebih dahulu lalu jalankan pengujian.

--compile :Sakelar ini berguna jika Anda ingin mengompilasi tanpa menjalankan tes untuk memeriksa kesalahan, misalnya.

Menjalankan pengujian di GUI simulator

Seringkali inspeksi visual dari gelombang diperlukan. VUnit menyediakan cara otomatis untuk menjalankan pengujian di simulator dengan menggunakan sakelar GUI -g . VUnit akan melakukan semua kompilasi dan meluncurkan ModelSim (simulator yang kami konfigurasikan) dengan kasus uji yang diminta dan menambahkan sinyal ke jendela gelombang, mengingat wave.do file tersedia.

python .\run.py *switch_1_on_output_check -g

Sekarang, VUnit akan meluncurkan ModelSim untuk kasus uji khusus ini, membuka jendela bentuk gelombang, dan menambahkan sinyal karena saya sudah memiliki motor_start_tb_wave.do di dalam gelombang folder.

Catatan: Anda dapat menyesuaikan bentuk gelombang sesuai keinginan, tetapi Anda harus menyimpan file format bentuk gelombang di dalam gelombang folder dengan konvensi nama ini testbench_file_name_wave.do sehingga dapat dimuat saat VUnit meluncurkan ModelSim.

Misalkan Anda mengubah kode Anda setelah menemukan kesalahan dan ingin menjalankan kembali kasus uji ini. Dalam hal ini, Anda dapat mengetik di jendela transkrip ModelSim:vunit_restart , Itu akan menyebabkan VUnit mengkompilasi ulang, memulai ulang, dan menjalankan kembali simulasi.

Pengemudi dan pemeriksa terkontrol dalam kasus uji

Sejauh ini, kita telah mempelajari cara menyiapkan testbench VUnit dan mengembangkan serta menjalankan test case. Di bagian ini, kami akan mengembangkan lebih banyak kasus uji dari sudut pandang insinyur verifikasi, menggunakan pendekatan verifikasi yang berbeda dan pustaka pemeriksa VUnit.

Tidak seperti kasus uji sebelumnya, pendekatan ini memiliki driver di dalam kotak uji dan pemeriksa di luar, tetapi kasus uji masih mengendalikannya.

Mari kita asumsikan kita memiliki persyaratan verifikasi ini:

Dari bagian DUT, kita mengetahui bahwa:

Kami akan menggunakan check_stable VUnit prosedur untuk memverifikasi bahwa:

Kami akan menggunakan check_next VUnit prosedur untuk memverifikasi bahwa:

check_stable :memverifikasi bahwa sinyal [s] stabil di dalam jendela yang dimulai dengan start_event pulsa sinyal dan diakhiri dengan end_event pulsa sinyal.

periksa_berikutnya :verifikasi bahwa sinyal ='1' setelah sejumlah siklus clock setelah start_event pulsa sinyal.

start_event dan end_event sinyal akan dikontrol dari test case.

Kita mulai dengan menambahkan sinyal yang diperlukan untuk check_stable dan check_next prosedur sebagai berikut:

-- VUnit signals
SIGNAL enable                  : STD_LOGIC := '0';
-- for yellow_led
SIGNAL yellow_next_start_event : STD_LOGIC := '0';
SIGNAL yellow_low_start_event  : STD_LOGIC := '0';
SIGNAL yellow_low_end_event    : STD_LOGIC := '0';
SIGNAL yellow_high_start_event : STD_LOGIC := '0';
SIGNAL yellow_high_end_event   : STD_LOGIC := '0';

Kemudian kita membuat test case baru di dalam test_suite menggunakan run VUnit fungsinya sebagai berikut:

ELSIF run("switch_2_on_output_check") THEN
  info("------------------------------------------------------------------");
  info("TEST CASE: switch_2_on_output_check");
  info("------------------------------------------------------------------");

Kami membuat start_event untuk status rendah YELLOW_LED untuk digunakan dengan check_stable prosedur sebagai berikut:

WAIT UNTIL reset <= '0';
-- out of reset
enable <= '1';
pulse_high(clk, yellow_low_start_event);
WAIT FOR C_CLK_PERIOD * 3;

enable sinyal mengaktifkan check_stable dan check_next prosedur, dan kami ingin mengaktifkannya dari awal.

pulse_high adalah prosedur sepele dari motor_tb_pkg yang menunggu tepi jam naik berikutnya dan mengirimkan sinyal untuk satu siklus jam. Dalam hal ini adalah yellow_ low_start_event .

Sekarang, kita hidupkan sakelar_1 dan tunggu hingga RED_LED menyala terus-menerus, lalu kita nyalakan sakelar_2:

-- turn ON switch_1
motor_start_in.switch_1 <= '1';
-- wait until RED_LED finished
WAIT FOR C_CLK_PERIOD * 12;
-- turn ON switch_2
motor_start_in.switch_2 <= '1';

Sekarang kita tahu bahwa setelah 5 siklus clock, YELLOW_LED akan menjadi '1'. Oleh karena itu, kami membuat start_event untuk YELLOW_LED untuk digunakan dengan check_next prosedur:

-- after 5 clk cycles YELLOW_LED will light
-- next_start_event for YELLOW_LED high
pulse_high(clk, yellow_next_start_event); -- 1st clk cycle

Demikian pula, kami membuat end_event untuk status rendah YELLOW_LED untuk digunakan dengan check_stable prosedur:

WAIT FOR C_CLK_PERIOD * 3;

-- end event for YELLOW_LED low
pulse_high(clk, yellow_low_end_event);  -- 5th clk cycle

Sekarang, YELLOW_LED akan menjadi tinggi selama 10 siklus clock. Oleh karena itu, kami membuat start_event untuk status tinggi YELLOW_LED untuk check_stable prosedur:

-- YELLOW_LED is high for 10 clk cycles
-- start event for YELLOW_LED high
yellow_high_start_event <= '1';
WAIT UNTIL rising_edge(clk); -- 1st clk cycle
yellow_high_start_event <= '0';

Di sini saya tidak menggunakan pulse_high prosedur karena saya ingin pulsa terjadi sekarang, dan bukan di tepi jatuh berikutnya.

Dan kami membuat end_event untuk status tinggi YELLOW_LED untuk check_stable setelah 8 siklus clock sebagai berikut:

WAIT FOR C_CLK_PERIOD * 8;
-- end event for YELLOW_LED
pulse_high(clk, yellow_high_end_event); -- 10th clk cycle

Dengan itu, test case selesai. Kita hanya perlu menambahkan panggilan ke prosedur pemeriksa setelah proses seperti ini:

----------------------------------------------------------------------
-- Related YELLOW_LED check
----------------------------------------------------------------------
-- check that YELLOW_LED is low from start until switch_2 is ON
check_stable(clock       => clk, 
             en          => enable, 
             start_event => yellow_low_start_event, 
             end_event   => yellow_low_end_event, 
             expr        => motor_start_out.yellow_led, 
             msg         => result("YELLOW_LED Low before switch_2"),
             active_clock_edge => rising_edge,
             allow_restart     => false);

-- check that YELLOW_LED is high after switch_2 is ON
check_next(clock       => clk,
           en          => enable, 
           start_event => yellow_next_start_event, 
           expr        => motor_start_out.yellow_led, 
           msg         => result("for yellow_led is high after 5 clk"),
           num_cks     => 5, 
           allow_overlapping   => false, 
           allow_missing_start => true);

-- check that YELLOW_LED is high after for 10 clk cycle
check_stable(clk, enable, yellow_high_start_event, yellow_high_end_event,
             motor_start_out.yellow_led, 
             result("for YELLOW_LED High after switch_2"));

Catatan: allow_restart parameter di check_stable prosedur memungkinkan jendela baru untuk memulai jika start_event baru terjadi sebelum end_event .

Catatan: kita masukkan 5 di check_next prosedur karena YELLOW_LED akan menjadi tinggi setelah 5 siklus clock yellow_next_start_event .

Catatan: dua parameter terakhir di check_next adalah:

Sekarang, kita dapat menjalankan test case dari baris perintah seperti ini:

python .\run.py *switch_2_on_output_check -v

Dan hasilnya akan seperti berikut:

Kita dapat menjalankan test case di GUI simulator seperti ini:

python .\run.py *switch_2_on_output_check -g

Menghasilkan bentuk gelombang ini:

Catatan:start_event dan end_event sinyal untuk check_stable disertakan di jendela, dan sinyal yang dipantau direferensikan saat start_event='1' .

Dalam kasus uji ini, kami mengeluarkan operasi pemeriksaan dari kasus uji tetapi mengendalikannya dari kasus uji. Perhatikan juga bahwa kami tidak memeriksa fungsi RED_LED.

Driver di dalam test case dan pemeriksa yang memeriksa sendiri

Salah satu kelemahan dari pendekatan sebelumnya adalah kurang mudah dibaca. Kasus pengujian berisi informasi yang tidak menarik atau terkait dengan pengujian maupun fungsionalitasnya, seperti start_event dan end_event sinyal. Kami ingin menyembunyikan semua detail ini, hanya memiliki driver dalam kasus uji, dan membuat pemeriksa yang memeriksa sendiri.

Mari kita mulai dengan mendesain driver.

Prosedur VHDL adalah kandidat yang baik untuk itu. Jika Anda tidak tahu cara menggunakan prosedur VHDL, lihat tutorial ini.

Di bawah ini adalah prosedur switch_driver dari motor_tb_pkg .

PROCEDURE switch_driver(
  SIGNAL switches     : OUT MOTOR_START_IN_RECORD_TYPE;
  CONSTANT clk_period : IN TIME;
  CONSTANT sw1_delay  : IN INTEGER;
  CONSTANT sw2_delay  : IN INTEGER;
  CONSTANT sw3_delay  : IN INTEGER) IS
BEGIN
  IF (sw1_delay = 0) THEN
    WAIT FOR clk_period * sw1_delay;
    switches.switch_1 <= '1';
  ELSIF (sw1_delay = -1) THEN
    switches.switch_1 <= '0';
  END IF;
  IF (sw2_delay = 0) THEN
    WAIT FOR clk_period * sw2_delay;
    switches.switch_2 <= '1';
  ELSIF (sw2_delay = -1) THEN
    switches.switch_2 <= '0';
  END IF;
  IF (sw3_delay = 0) THEN
    WAIT FOR clk_period * sw3_delay;
    switches.switch_3 <= '1';
  ELSIF (sw3_delay = -1) THEN
    switches.switch_3 <= '0';
  END IF;
END PROCEDURE switch_driver;

Kami menyediakan periode jam untuk menghitung penundaan dan bilangan bulat yang menentukan penundaan yang diinginkan untuk setiap sakelar dalam periode jam.

Sekarang kita dapat menggunakan prosedur ini di dalam test case sebagai berikut:

switch_driver(motor_start_in,C_CLK_PERIOD,3,12,20);

Ini akan mengaktifkan sakelar_1 setelah 3 siklus clock, lalu mengaktifkan sakelar_2 setelah 12 siklus clock, dan terakhir, mengaktifkan sakelar_3 setelah 20 siklus clock.

Sejauh ini, kami telah menggunakan pemeriksa default VUnit. VUnit menyediakan kemungkinan untuk memiliki pemeriksa kustom. Ini berguna ketika Anda memiliki kasus uji besar dengan banyak pemeriksaan, dan Anda ingin memiliki statistik tentang pemeriksaan, atau Anda tidak ingin pemeriksaan tercampur dengan sisa testbench.

Kami akan membuat pemeriksa khusus untuk RED_LED dan menyetel tingkat log yang gagal ke PERINGATAN:

CONSTANT RED_CHECKER : checker_t := new_checker("red_led_checker", WARNING);

Dan kami mengaktifkan logging melewati pemeriksaan untuk RED_CHECKER di bagian Setup VUnit:

show(get_logger(RED_CHECKER), display_handler, pass);

Sekarang mari kita beralih ke pemeriksa pemeriksaan mandiri (atau monitor). Kami akan merancang pemeriksa pemeriksaan mandiri untuk RED_LED terlebih dahulu, dan kami akan menggunakan proses untuk ini sebagai berikut:

red_monitor_process : PROCESS
BEGIN
  WAIT UNTIL reset = '0';
  pulse_high(clk, led_low_start_event);
  WAIT UNTIL motor_start_in.switch_1 = '1';
-- RED_LED is blinking
FOR i IN 0 TO 4 LOOP
  IF (motor_start_in.switch_1 = '1' AND
      motor_start_in.switch_2 = '0' AND
      motor_start_in.switch_3 = '0') THEN

    WAIT UNTIL rising_edge(clk);
    WAIT FOR 1 ps;
    check(RED_CHECKER, motor_start_out.red_led = '1', 
          result("for red_led blink high"));

    WAIT UNTIL rising_edge(clk);
    WAIT FOR 1 ps;
    check(RED_CHECKER, motor_start_out.red_led = '0',
          result("for red_led blink low"));

    -- RED_LED is constantly lighting start event
    IF (i = 4) THEN -- finish blinking
      WAIT UNTIL rising_edge(clk);
      WAIT FOR 1 ps;
      check(RED_CHECKER, motor_start_out.red_led = '1',
            result("for red_led blink low"));
      pulse_high(clk, red_high_start_event);
    END IF;
  ELSE
  -- perform check for error (All led high until all switches are off)
  END IF;
END LOOP;
  IF (motor_start_in.switch_2 /= '1') THEN
    WAIT UNTIL motor_start_in.switch_2 = '1';
  END IF;
  WAIT UNTIL rising_edge(clk);
  WAIT FOR C_CLK_PERIOD * 14;
  -- RED_LED is constantly lighting end event
  pulse_high(clk, red_high_end_event);
END PROCESS red_monitor_process;
-- check that RED_LED is low from start until switch_1 is ON
-- Note the use of motor_start_in.switch_1 as end_event
check_stable(RED_CHECKER, clk, enable, led_low_start_event, 
             motor_start_in.switch_1, motor_start_out.red_led,
             result("RED_LED low before switch_1"));

-- check that RED_LED is low after switch_2 is ON
check_stable(RED_CHECKER, clk, enable, red_high_start_event, 
             red_high_end_event, motor_start_out.red_led,
             result("RED_LED high after switch_1"));

Mari kita uji ini dengan kasus uji berikut:

ELSIF run("red_led_output_self-check") THEN
  info("---------------------------------------------------------------");
  info("TEST CASE: red_led_output_self-check");
  info("---------------------------------------------------------------");
  WAIT UNTIL reset = '0';
  -- turn switch_1 on after 3 clk cycles and switch_2 after 13 clk cycles
  switch_driver(motor_start_in,C_CLK_PERIOD,3,13,-1);

  WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
  info("===== TEST CASE FINISHED =====");

Kami menjalankan simulasi seperti ini:

python .\run.py *red_led* -v

Hasilnya adalah:

Catatan bahwa pemeriksa sekarang red_led_checker .

Kita dapat mengikuti pendekatan yang sama untuk merancang pemeriksa pemeriksaan mandiri untuk YELLOW_LED, tetapi saya akan membiarkan ini sebagai latihan untuk pembaca. Namun, saya akan menunjukkan cara berbeda berikutnya untuk memverifikasi fungsionalitas GREEN_LED menggunakan check , check_stable , check_next , dan check_equal prosedur.

Untuk memverifikasi bahwa GREEN_LED OFF dari awal sampai switch_3 pertama kali ON, kita cukup menggunakan check_stable prosedur:

-- check that GREEN_LED is low from start until switch_3 is ON.
check_stable(clk, enable, led_low_start_event, 
             motor_start_in.switch_3, motor_start_out.green_led,
             result("GREEN_LED low before switch_3"));

Salah satu cara untuk memverifikasi bahwa GREEN_LED AKTIF saat sakelar_3 AKTIF adalah menggunakan check_next prosedur. Kami menyebutnya dengan switch_3 sebagai start_event , tetapkan 1 siklus jam ke num_cks , dan izinkan tumpang tindih:

-- check that GREEN_LED is high using check_next
check_next(clock       => clk, 
           en          => green_next_en,
           start_event => motor_start_in.switch_3,
           expr        => motor_start_out.green_led,
           msg         => result("for green_led high using check_next"),
           num_cks     => 1,
           allow_overlapping   => true,
           allow_missing_start => false);

Karena kami mengizinkan tumpang tindih, prosedur ini akan dipicu pada setiap tepi naik jam ketika switch_3 adalah '1'.t akan mengharapkan bahwa GREEN_LED adalah '1' setelah satu siklus jam, dan inilah yang kami inginkan.

Cara lain untuk menguji fungsionalitas GREEN_LED adalah menggunakan versi jam dari check prosedur dengan versi switch_3 yang tertunda sebagai Aktifkan untuk prosedur ini:

switch_3_delayed <= motor_start_in.switch_3'delayed(C_CLK_PERIOD + 1 ps)
                    AND NOT enable;
check(clock => clk,
      en    => switch_3_delayed,
      expr  => motor_start_out.green_led,
      msg   => result("for green_led high using delayed"));

switch_3_delayed adalah sinyal tertunda siklus 1 jam dari switch_3. Dengan demikian prosedur ini akan selalu mengaktifkan 1 siklus clock setelah switch_3 adalah '1', dan prosedur memeriksa apakah GREEN_LED adalah '1' ketika diaktifkan.

switch_3_delayed sinyal adalah switch_3 versi tertunda oleh satu siklus clock. Dengan demikian akan memungkinkan prosedur ini selalu satu siklus clock setelah switch_3 adalah '1'. Saat diaktifkan, prosedur akan memeriksa apakah GREEN_LED adalah '1'.

Catatan: AND NOT aktifkan di switch_3_delayed hanya untuk menutupi sinyal ini saat kita tidak menggunakan pendekatan proses.

Terakhir, kita dapat menggunakan proses khusus dengan loop While VHDL untuk melakukan pemeriksaan GREEN_LED:

green_monitor_process : PROCESS
BEGIN
  WAIT UNTIL enable = '1' AND 
             motor_start_in.switch_1 = '1' AND
             motor_start_in.switch_2 = '1' AND
             motor_start_in.switch_3 = '1';
  WHILE motor_start_in.switch_3 = '1' LOOP
    WAIT UNTIL rising_edge(clk);
    WAIT FOR 1 ps;
    check_equal(led_out, STD_LOGIC_VECTOR'("100"), 
                result("for led_out when switch_3 on"));
  END LOOP;
END PROCESS green_monitor_process;

Sekarang kami mengembangkan kasus uji untuk GREEN_LED sebagai berikut:

ELSIF run("switch_3_on_output_check") THEN
  info("-------------------------------------------------------------");
  info("TEST CASE: switch_3_on_output_check");
  info("-------------------------------------------------------------");
  info("check using a clocked check PROCEDURES");
  WAIT UNTIL reset = '0';
  switch_driver(motor_start_in,C_CLK_PERIOD,3,12,20);
  WAIT FOR C_CLK_PERIOD * 5;
  switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
  WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
  info("-------------------------------------------------------------");
  
  info("check using check_next PROCEDURES");
  green_next_en <= '1';
  switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,10);
  WAIT FOR C_CLK_PERIOD * 5;
  switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
  WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
  green_next_en <= '0';
  info("-------------------------------------------------------------");
  
  info("check using check_equal process");
  enable <= '1';
  switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,10);
  WAIT FOR C_CLK_PERIOD * 5;
  switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
  WAIT FOR C_CLK_PERIOD * 10; -- dummy wait
  info("===== TEST CASE FINISHED =====");

Setelah menulis kasus uji, Anda dapat menjalankannya dan memeriksa hasil yang berbeda.

Kita dapat melihat bahwa kasus uji dalam pendekatan pemeriksaan mandiri jauh lebih mudah dibaca, bahkan untuk insinyur yang tidak memiliki pengetahuan tentang VHDL.

Ada kasus uji lain di file testbench. Kita dapat memulainya menggunakan perintah ini:

python .\run.py *motor_start_tb* --list

Dan ini adalah output yang dicetak ke konsol:

tb_lib.motor_start_tb.switches_off_output_check
tb_lib.motor_start_tb.switch_1_on_output_check
tb_lib.motor_start_tb.switch_2_on_output_check
tb_lib.motor_start_tb.red_led_output_self-check
tb_lib.motor_start_tb.switch_3_on_output_check
tb_lib.motor_start_tb.switch_2_error_output_check
Listed 6 tests

Kita dapat menjalankan semua kasus uji dengan mengetik:

python .\run.py

Dan hasilnya adalah sebagai berikut:

==== Summary =============================================================
pass tb_lib.motor_start_tb.switch_1_on_output_check    (0.4 seconds)
pass tb_lib.motor_start_tb.switch_2_on_output_check    (0.4 seconds)
pass tb_lib.motor_start_tb.red_led_output_self-check   (0.4 seconds)
pass tb_lib.motor_start_tb.switch_3_on_output_check    (0.4 seconds)
fail tb_lib.motor_start_tb.switches_off_output_check   (0.9 seconds)
fail tb_lib.motor_start_tb.switch_2_error_output_check (0.4 seconds)
==========================================================================
pass 4 of 6
fail 2 of 6
==========================================================================
Total time was 2.9 seconds
Elapsed time was 2.9 seconds
==========================================================================
Some failed!

Ringkasan

Kerangka kerja VUnit menyediakan fitur-fitur canggih untuk otomatisasi pengujian yang berjalan dan perpustakaan yang kaya untuk pengembangan testbenches. Selain itu, ini memungkinkan insinyur desain untuk menguji desain mereka lebih awal dan sering.

VUnit juga membantu insinyur verifikasi mengembangkan dan menjalankan testbenches lebih cepat dan lebih mudah. Yang terpenting, ia memiliki kurva belajar yang cepat dan ringan.

Ke mana harus pergi dari sini

Unduh contoh proyek dengan menggunakan formulir di bawah ini!


VHDL

  1. Code Ready Containers:Memulai Perkakas Otomatisasi Proses di Cloud
  2. Memulai pencetakan 3D keramik
  3. Berkenalan dengan Pewarna Dasar!
  4. Memulai TJBot
  5. Memulai RAK 831 Lora Gateway dan RPi3
  6. Memulai RAK831 LoRa Gateway dan RPi3
  7. Memulai bisnis dengan IoT
  8. Memulai Dengan AI Dalam Asuransi:Panduan Pengantar
  9. Tutorial Arduino 01:Memulai
  10. Memulai My.Cat.com