Quartus Prime IP Bibliotheken in VUnit integrieren: Schritt-für-Schritt-Anleitung
Haben Sie jemals gewollt, eine VHDL-Simulation durchzuführen, die einen Quartus IP-Kern über das VUnit-Verifikationsframework einbindet?
Genau das hatte FPGA-Ingenieur Konstantinos Paraskevopoulos im Sinn, fand aber kein passendes Tutorial. Glücklicherweise nutzte er sein Talent, um herauszufinden, wie es geht, und war so freundlich, es über diesen Gastartikel mit VHDLwhiz zu teilen.
Überlassen wir das Wort Konstantinos!
Es ist oft wünschenswert, vordefinierte IPs aus dem Quartus IP-Katalog in das Design aufzunehmen, wenn das System mit VUnit simuliert wird. Dieses Tutorial vermittelt das Wissen, um Quartus IP-Bibliotheken zu generieren, einzubinden und mit der VUnit-Umgebung zu verknüpfen.
Neu bei VUnit? Schauen Sie sich dieses Tutorial an: Getting started with VUnit
Überblick
Dieses Tutorial besteht aus drei Hauptteilen:
- Kurze Beschreibung des gewählten IPs
- Schritte zum Generieren und Verknüpfen der passenden Bibliotheken
- Verifikation durch Nutzung von VUnit und ModelSim
Voraussetzungen
- Quartus
- Quartus Prime herunterladen
- QSYS sollte in Ihrem PATH für SCHRITT 2 (CMD-Option) sein
- Intel ModelSim
- Siehe diesen Artikel für kostenlose ModelSim-Installation
- ModelSim sollte in Ihrem PATH sein
- VUnit
- Siehe diesen Artikel für kostenlose VUnit-Installation
- Python 3.6 oder höher
- Python herunterladen
- Python sollte in Ihrem Pfad sein
Es wird auch grundlegendes VHDL-Wissen und ModelSim-Kenntnisse vorausgesetzt.
Design under Test
Für unser Szenario nutzen wir den Parallel Adder IP aus der Quartus Integer Arithmetic IP-Liste.

Unser Design akzeptiert drei 16-Bit-Eingangsvektoren und gibt das addierte Ergebnis in einem 17-Bit-Vektor aus.
Schritt 1: IP generieren
Wir generieren unseren Adder im IP-Katalog-Fenster per Doppelklick auf die Parallel-Adder-Komponente unter Library/Basic functions/Arithmetic.
Nach Vergabe eines Namens und Anpassung der Komponente an unsere Bedürfnisse klicken wir unten rechts auf den „Generate HDL“-Button.

An diesem Punkt erscheint ein Fenster, wie in der folgenden Abbildung dargestellt.
Hinweis: Wir müssen Create simulation model unter dem Simulation-Abschnitt auf entweder VHDL oder Verilog setzen, um die Simulationsdateien zu generieren, da die Standardoption „none“ ist. Wählen wir keines, wird die given_ip_name.spd-Datei nicht generiert, was den nächsten Schritt scheitern lässt.

Der obige Prozess generiert eine Datei und einen Ordner unter unserem quartus-Verzeichnis:
- Datei:
given_ip_name.ip - Ordner:
given_ip_name
Der Ordner enthält .vhd und .v Dateien, die später in unser run.py Skript eingefügt werden müssen.
Schritt 2: IP-Simulationsdateien generieren
- GUI: Tools ➤ Generate Simulator Setup Script for IP auswählen und Ausgabeverzeichnis im Prompt-Fenster angeben,
- CMD: Mittels Qsys-Befehlen können wir dieselben Dateien erzeugen, indem wir im Terminal folgenden Befehl eingeben:
ip-setup-simulation --quartus-project= <project's_QPF_filepath> \ --output-directory= <my_dir>
Mit einer der beiden Methoden weisen wir Quartus an, ein Verzeichnis für jeden unterstützten Simulator zu generieren, das ein Skript zur Erstellung und Kompilierung der IP-Bibliotheken enthält.
Schritt 3: IP-Bibliotheken für ModelSim generieren und kompilieren
Der nächste Schritt ist, das msim_setup.tcl Skript im mentor Ordner zu finden, der im vorherigen Schritt erstellt wurde, und es unter dem Namen setup.tcl zu duplizieren. Dann in der setup.tcl Datei die illustrierten Befehle auskommentieren und die $QSYS_SIMDIR Variable setzen.
# # QSYS_SIMDIR is used in the Quartus-generated IP simulation script to # # construct paths to the files required to simulate the IP in your Quartus # # project. By default, the IP script assumes that you are launching the # # simulator from the IP script location. If launching from another # # location, set QSYS_SIMDIR to the output directory you specified when you # # generated the IP script, relative to the directory from which you launch # # the simulator. # set QSYS_SIMDIR <script generation output directory> # # # Source the generated IP simulation script. source $QSYS_SIMDIR/mentor/msim_setup.tcl # # # Set any compilation options you require (this is unusual). # set USER_DEFINED_COMPILE_OPTIONS <compilation options> # set USER_DEFINED_VHDL_COMPILE_OPTIONS <compilation options for VHDL> # set USER_DEFINED_VERILOG_COMPILE_OPTIONS <compilation options for Verilog> # # # Call command to compile the Quartus EDA simulation library. dev_com # # # Call command to compile the Quartus-generated IP simulation files. com #
Nach Anpassung und Speichern der setup.tcl können wir die Tcl-Datei sicher mit dem vsim Befehl ausführen.
vsim -c -do "do setup.tcl; quit"
Das generiert die kompilierten Bibliotheken im mentor Ordner.
Schritt 4: VUnit Verknüpfung
Da die IP-Bibliotheken generiert sind, sollten wir sie mittels des Python run.py Skripts verknüpfen.
Schauen Sie sich die Abbildung unten an, um die Verzeichnisstruktur unseres Beispiels besser zu verstehen. Die initiale Topologie bestand aus dem Root-Ordner demo, den Ordnern tb, vunit und quartus. Alle Unterordner und Dateien unter quartus wurden durch das Quartus-Framework nach Projekterstellung und Abschluss der Schritte 1 bis 3 generiert.
Hinweis: Quartus generiert mehr Dateien und Ordner, aber das Bild unten zeigt die für uns relevanten.

Mit dieser eindeutigen Ansicht der Topologie als Referenz können wir unseren ROOT-Pfad und den Pfad(e) zu den generierten Bibliotheken angeben, wie unten gezeigt.
Beachten Sie, dass sim_files das Verzeichnis ist, das wir in Schritt 2 angegeben haben, wo der mentor-Ordner gespeichert wurde.
from vunit import VUnit from os.path import join, dirname, abspath # ROOT root = join(dirname(__file__), '../') # Path to generated libraries path_2_lib = '/quartus/sim_files/mentor/libraries/' # ROOT
Nach Erstellung einer VUnit-Instanz namens vu können wir eine Designbibliothek für unseren VHDL-Code angeben und beliebige externe Bibliotheken verknüpfen:
# Create VUnit instance by parsing command line arguments vu = VUnit.from_argv() # create design's library my_lib = vu.add_library('my_lib') # Link external library vu.add_external_library("parallel_adder", root + path_2_lib + "parallel_adder")
Und schließlich fügen wir unsere Quelldateien hinzu. Diese befinden sich in drei Unterordnern unter dem given_ip_name Verzeichnis:
parallel_add_191synthsim
Die synth und sim Verzeichnisse enthalten dieselben Informationen, nämlich das Top-Level-Design unseres IP. Die Formatierung dieser Dateien ist in unserem Fall VHDL. Sie könnten in Verilog sein, abhängig von der gewählten Sprache in Schritt 1.
Falls unser Top-Level-Design Sub-Komponenten umfasst, müssen wir auch deren Quelldateien einbinden. Sie befinden sich in Unterordnern im given_ip_name Verzeichnis, wie die parallel_add_191 Komponente in unserem Fall.
my_lib.add_source_files(join(root,'quartus','parallel_adder','sim','parallel_adder.vhd')) my_lib.add_source_files(join(root,'quartus','parallel_adder','parallel_add_191','sim','parallel_adder_parallel_add_191_oh4guxa.vhd')) my_lib.add_source_files(join(root,'tb','tb_demo.vhd')) testbench = my_lib.entity("tb_demo") vu.main()
Testbench
Zunächst können Sie diesen Link besuchen, um die Grundlagen der VUnit-Testbench-Bildung zu lernen.
Zurück zu unserer Testbench fügen wir die notwendigen VUnit-Bibliotheken sowie jede andere Bibliothek hinzu, die wir nutzen möchten, und definieren unsere Signale.
Hinweis: Die Prozessexekution in unserem Beispiel ist sequenziell. Daher werden Steuersignale (als Flags bezeichnet) verwendet, um einen Prozess zu benachrichtigen, ob er starten oder terminieren soll.
library IEEE; use IEEE.std_logic_1164.all; use ieee.numeric_std.all; library vunit_lib; context vunit_lib.vunit_context; entity tb_demo is generic ( runner_cfg : string:= runner_cfg_default); end tb_demo; architecture sim of tb_demo is constant clk_period : time := 10 ns; signal clk : std_logic := '0'; signal rst : std_logic := '0'; -- INPUTS signal data_a : std_logic_vector(0 to 15):= (others => '0'); signal data_b : std_logic_vector(0 to 15):= (others => '0'); signal data_c : std_logic_vector(0 to 15):= (others => '0'); -- OUTPUTS signal result : std_logic_vector(0 to 16); -- CONTROL FLAGS signal reset_done :boolean := false; signal sim_done :boolean := false; signal start_sim :boolean := false;
Anschließend instantiieren wir unser UUT. Quartus liefert Komponenten-Instantiierungsbeispiele für VHDL und Verilog unter den Dateinamenskonventionen ip_name_inst.vhd und ip_name_inst.v.
begin -- Unit Under Test UUT : entity work.parallel_adder port map ( data0x => data_a, -- parallel_add_input.data0x data1x => data_b, -- .data1x data2x => data_c, -- .data2x result => result -- parallel_add_output.result );
Die ersten beiden Prozesse, die starten, sind clk_process und reset_rel. Während der letztere nach Reset und Setzen des reset_done Flags auf true suspendiert wird, operiert der clk_process über die gesamte Simulationszeit.
clk_process : process begin clk <= '1'; wait for clk_period/2; clk <= '0'; wait for clk_period/2; end process clk_process; reset_rel : process begin rst <= '1'; wait for clk_period*2; wait until rising_edge(clk); rst <= not rst; reset_done <= true; wait; end process reset_rel;
Nun, da der Reset erledigt ist, können wir den test_runner Prozess für die Ausführung unserer Tests aufrufen. Darüber hinaus bleibt der Test Runner aktiv, bis das sim_done Flag auf true gesetzt wird, was im letzten Prozess geschieht.
test_runner : process begin test_runner_setup(runner, runner_cfg); wait until reset_done and rising_edge(clk); iterate : while test_suite loop start_sim <= true; if run("test_case_1") then info ("Start"); info (running_test_case); wait until sim_done; end if; end loop; test_runner_cleanup(runner); end process test_runner;
Schließlich führt der data_generator Prozess mehrere Additionen aus, indem er Werte den drei Eingängen unseres Parallel Adders zuweist, mittels einer for Schleife.
Hinweis: Dieser Prozess wird ausgelöst, wenn der test_runner Prozess anweist, indem er das start_sim Flag setzt. Am Ende dieses Prozesses setzt er das sim_done Flag, was den Test Runner veranlasst, die Simulation anzuhalten.
data_generator : process constant tag2 : log_level_t := new_log_level("INFO", fg => blue, bg => black, style => bright); variable a,b,c,d : integer; begin wait until start_sim; wait until rising_edge(clk); show(display_handler, tag2); if running_test_case = "test_case_1" then for i in 0 to 10 loop data_a <= std_logic_vector(to_unsigned(i+10,data_a'length)); data_b <= std_logic_vector(to_unsigned(i+20,data_a'length)); data_c <= std_logic_vector(to_unsigned(i+30,data_a'length)); wait until rising_edge(clk); a := to_integer(unsigned(data_a)); b := to_integer(unsigned(data_b)); c := to_integer(unsigned(data_c)); d := to_integer(unsigned(result)); log( integer'image(a) &" + " & integer'image(b) &" + " & integer'image(c) &" = " & integer'image(d), tag2); end loop; end if; sim_done <= true; end process data_generator;
Verifikation
Um den Testfall auszuführen und zu verifizieren, dass alles wie erwartet funktioniert, können wir das run.py Skript aus dem Verzeichnis, in dem es sich befindet, einfach durch Eingabe des folgenden Befehls im Terminal ausführen.
python ./run.py -v
Hinweis: Ein angepasster Logger wurde für bessere Illustration in unserer Ausgabe verwendet, der durch die verbose -v Option sichtbar wird. Da nur ein Testfall definiert ist, müssen wir keine Option angeben, um ihn zu spezifizieren.

Schließlich können wir zur Verifikation unserer Ergebnisse in ModelSim folgenden Befehl eingeben:
python ./run.py --gui

(Bild anklicken zum Vergrößern)
Fazit
Zusammenfassend haben wir gelernt, wie man Quartus IPs, die im IP-Katalog residieren, in eine VUnit-Umgebung einbindet und testet. Obwohl dieses Tutorial ein vordefiniertes IP nutzte, gilt derselbe Prozess für custom packaged IPs.
Schauen Sie sich dieses VUnit Tutorial an, falls Sie es noch nicht getan haben:
Getting started with VUnit
VHDL
- SigFox erklärt: Technologie, Markteinfluss und Vergleich zu Link Labs
- VUnit 101: Praktischer Leitfaden für automatisierte VHDL-Verifikation
- String-Listen in VHDL erstellen: Best Practices & Beispiel
- VHDL-Testbench-Simulation sauber beenden
- PWM-Controller in VHDL implementieren: Design, Simulation & FPGA-Demo
- Selbstprüfenden VHDL-Testbench erstellen – Praxisleitfaden
- Technologie: Der Schlüssel zu transparenten, ethischen Lieferketten
- Kettengliedzaun-Maschine: Funktionsweise & Vorteile
- Hydraulik-Zahnradpumpe entlüften (primen) für optimale Leistung
- Schritt-für-Schritt-Anleitung: Ford Traktor Hydraulikpumpe entlüften (primen)