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:
Intel ModelSim
Silakan lihat artikel ini untuk cara menginstal ModelSim secara gratis.
ModelSim harus ada di PATH Anda.
Python 3.6 atau lebih tinggi.
Unduh Python
Python harus ada di PATH Anda.
GIT (opsional).
Unduh GIT
Terminal Windows (opsional)
Unduh terminal Windows
Ini juga mengasumsikan memiliki pengetahuan dasar VHDL dan keterampilan ModelSim.
Memasang
Mendapatkan VUnit:
Jika Anda memiliki GIT, Anda dapat mengkloningnya dari GitHub ke drive C Anda:
Jika tidak, Anda dapat mengunduhnya sebagai Zip dari GitHub dan mengekstraknya ke drive C Anda:
Unduh VUnit.
Memasang VUnit:
Buka terminal dan navigasikan ke C:\vunit dan ketik perintah berikut:
python setup.py install
Mengonfigurasi VUnit:
Tambahkan variabel lingkungan ke sistem Anda seperti di bawah ini.
VUNIT_MODELSIM_PATH :jalur ke ModelSim di mesin Anda.
VUNIT_SIMULATOR :ModelSim
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:
Perpustakaan Python: menyediakan alat yang membantu dengan otomatisasi pengujian, administrasi, dan konfigurasi.
Perpustakaan VHDL: kumpulan library yang membantu tugas verifikasi umum.
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:
Memuat konfigurasi
Kalibrasi
Rotasi
Urutan startup
Berikut urutan startup motor dan arti dari indikator LED.
RED_LED mewakili konfigurasi pemuatan .
Aktifkan sakelar_1.
RED_LED akan mulai berkedip 5 kali.
Tunggu hingga RED_LED berhenti berkedip dan terus menyala.
LED_KUNING mewakili kalibrasi pemuatan .
Sekarang Anda dapat mengaktifkan switch_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.
GREEN_LED menunjukkan bahwa motor sedang berputar.
Sekarang, motor siap digunakan. Setiap kali sakelar_3 AKTIF, GREEN_LED akan mulai menyala terus-menerus hingga sakelar_3 OFF.
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:
Menyiapkan skrip menjalankan Python
Menyiapkan kerangka VUnit
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.
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 .
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:
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.
motor_start dan motor_pkg akan dikompilasi menjadi DESIGN_LIB .
motor_start_tb dan motor_tb_pkg akan dikompilasi menjadi TB_LIB .
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:
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:
Pengemudi dan pemeriksa dalam kasus uji.
Pengemudi dan pemeriksa terkontrol dalam kasus uji.
Pengemudi dalam kasus uji dan pemeriksa pemeriksaan mandiri.
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':
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:
Pastikan output setelah AKTIFKAN sakelar_2 saat sakelar_1 AKTIF dan RED_LED menyala.
Dari bagian DUT, kita mengetahui bahwa:
Saat sakelar_2 AKTIF, YELLOW_LED akan mulai menyala terus-menerus setelah 5 siklus clock selama 10 siklus clock, dan setelah itu, YELLOW_LED dan RED_LED akan MATI.
Kami akan menggunakan check_stable VUnit prosedur untuk memverifikasi bahwa:
YELLOW_LED adalah ‘0’ dari awal hingga sakelar_2 AKTIF.
YELLOW_LED adalah '1' untuk 10 siklus clock.
Kami akan menggunakan check_next VUnit prosedur untuk memverifikasi bahwa:
YELLOW_LED adalah '1' setelah 5 siklus clock dari menyalakan sakelar_2.
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:
allow_overlapping :izinkan start_event kedua sebelum expr untuk yang pertama adalah '1'.
allow_missing_start :izinkan expr menjadi '1' tanpa start_event .
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.
Nilai alami (>=0) artinya:nyalakan sakelar setelah (clk_period * sw_delay ).
Nilai -1 artinya:matikan sakelar.
Semua nilai negatif lainnya berarti:tidak melakukan apa-apa.
Sekarang kita dapat menggunakan prosedur ini di dalam test case sebagai berikut:
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:
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:
Tunggu sampai switch_1 AKTIF dan tambahkan start_event untuk semua LED rendah:
red_monitor_process : PROCESS
BEGIN
WAIT UNTIL reset = '0';
pulse_high(clk, led_low_start_event);
WAIT UNTIL motor_start_in.switch_1 = '1';
Kami menggunakan FOR LOOP yang sama dari kasus uji pertama untuk RED_LED yang berkedip dan menambahkan start_event untuk RED_LED tinggi:
-- 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;
Sekarang kita menunggu switch_2 AKTIF, lalu kita tambahkan end_event untuk RED_LED tinggi:
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;
Sekarang kita tambahkan check_stable prosedur untuk RED_LED tinggi dan rendah:
-- 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:
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
Dokumentasi VUnit
Blog VUnit
VUnit gitter
Unduh contoh proyek dengan menggunakan formulir di bawah ini!