Verwenden von Quartus und ModelSim

Ich habe mich schon immer für digitale Schaltkreise interessiert, insbesondere für Hardwarebeschreibungssprachen - HDL. Ich hatte ein Buch von David M. Harris und Sarah L. Harris, "Digitale Schaltkreise und Computerarchitektur", für eine lange Zeit auf meiner Liste zukünftiger Lektüre, wobei ich die Freizeit der Selbstisolation nutzte und zu diesem wunderbaren Buch kam. Beim Lesen stieß ich auf einige Schwierigkeiten, insbesondere beim Schreiben und Debuggen von Code in Quartus Prime. Bei der Suche hat mir die Website marsohod.org wirklich geholfen .Der Prozess der Schaltungssimulation auf dieser Site wird jedoch mit den in Quartus integrierten Tools beschrieben. In modernen Versionen des Programms gibt es keine solchen integrierten Tools, und Sie müssen ModelSim verwenden. Um das Wissen, das ich mit Quartus und ModelSim gewonnen habe, irgendwie zu systematisieren, habe ich beschlossen, diesen Artikel zu schreiben. Im Verlauf dieses Artikels werde ich als Beispiel das Problem aus dem Buch "Digitale Schaltkreise und Computerarchitektur" von David M. Harris und Sarah L. Harris analysieren, insbesondere Problem 3.26 über die Sodawassermaschine. In diesem Artikel werde ich Ihnen zeigen, wie Sie Quartus installieren, ein Projekt erstellen, Code schreiben und simulieren. Jeder, der sich dafür interessiert, ist unter der Katze willkommen.



Bild



Formulierung des Problems



Sie wurden überredet, eine Softdrink-Maschine für das Büro zu entwerfen. Getränke werden teilweise von der Gewerkschaft übernommen, daher kosten sie nur 5 Rubel. Die Maschine akzeptiert Münzen von 1, 2 und 5 Rubel. Sobald der Kunde den erforderlichen Betrag bezahlt, gibt die Maschine das Getränk aus und gibt das Wechselgeld zurück. Entwerfen Sie einen Zustandsautomaten für einen Automaten für alkoholfreie Getränke. Die Eingaben der Maschine sind 1, 2 und 5 Rubel, nämlich welche dieser Münzen eingelegt ist.



Angenommen, für jedes Taktsignal wird nur eine Münze eingelegt. Die Maschine hat Ausgänge: Soda einschenken, 1 Rubel zurückgeben, 2 Rubel zurückgeben, 2 bis 2 Rubel zurückgeben. Sobald 5 Rubel (oder mehr) in der Maschine gesammelt sind, setzt sie das Signal „Pour GASING“ sowie Signale, die die entsprechende Änderung zurückgeben. Der Automat muss dann bereit sein, wieder Münzen anzunehmen.



Theorie



Finite-State-Maschinen oder Finite-State-Maschinen (FSMs) gehören zur Klasse der synchronen sequentiellen Schaltungen, die die überwiegende Mehrheit der digitalen Schaltungen darstellen. So sollten Sie Ihre Projekte umsetzen (zumindest anfangs). Dieses Verfahren bietet Wiederholbarkeit und Überprüfung der Schaltung und hängt nicht von den Verzögerungsverhältnissen verschiedener Schaltungselemente ab. Die Regeln zum Aufbau synchroner sequentieller Schaltungen besagen, dass eine Schaltung eine synchrone sequentielle Schaltung ist, wenn ihre Elemente die folgenden Bedingungen erfüllen:



  • Jedes Element der Schaltung ist entweder ein Register oder eine Kombinationsschaltung.
  • Mindestens ein Schemaelement ist ein Register.
  • Alle Register werden mit einem einzigen Taktsignal getaktet.
  • Jeder zyklische Pfad enthält mindestens ein Register.


Eine Zustandsmaschine hat mehrere Zustände, die sie in Registern speichert. Wenn ein Taktsignal eintrifft, kann die Zustandsmaschine ihren Zustand ändern, und wie sich der Zustand genau ändert, hängt von den Eingangssignalen und dem aktuellen Zustand ab. Im einfachsten Fall gibt es möglicherweise überhaupt keine Eingangssignale, sodass der Frequenzteiler funktioniert. Es gibt zwei Hauptklassen von Zustandsautomaten: den Moore-Automaten, bei dem die Ausgangssignale nur vom aktuellen Zustand des Automaten abhängen, und den Mealy-Automaten, bei dem die Ausgangssignale vom aktuellen Zustand und den Eingangssignalen abhängen. Im Prinzip kann jede endliche Zustandsmaschine sowohl nach dem Moore-Schema als auch nach dem Mealy-Schema implementiert werden. Der Unterschied zwischen ihnen besteht darin, dass der Moore-Automat mehr Zustände hat und eine Uhr hinter der Mealy-Maschine liegt.Für die Soda-Maschine werde ich die Miles-Schaltung verwenden. Schreiben wir die Zustände der Zustandsmaschine auf:

Symbol Beschreibung
S 0 Ausgangszustand, akkumulierte Menge 0 Rubel.
S 1 Die angesammelte Menge von 1 reiben.
S 2 Kumulierte 2 Rubel.
S 3 3 Rubel angesammelt.
S 4 4 Rubel angesammelt.


Das Eingangssignal ist ein Zwei-Bit-Bus mit der folgenden Codierung der Münzbezeichnung:

Symbol Wert Beschreibung
I 1 01 1 RUB
I 2 zehn 2 reiben
I 5 elf 5 reiben


Zeichnen wir ein Zustandsdiagramm unseres Automaten (in den Zustandsdiagrammen des Mealy-Automaten müssen die Ausgangssignale auf den Zustandsübergangspfeilen angegeben werden. Ich werde dies nicht tun, um das Diagramm nicht zu überladen, alle Ausgangssignale werden in der folgenden Tabelle beschrieben):



Bild



Schreiben wir die Tabelle der Zustandsänderungen und Ausgangssignale auf:

Zustände Eingangssignale
S S' insert pour_water C 1 . change1 2 . change2 2 2 . change22
S0 S1 I1 0 0 0 0
S0 S2 I2 0 0 0 0
S0 S0 I5 1 0 0 0
S1 S2 I1 0 0 0 0
S1 S3 I2 0 0 0 0
S1 S0 I5 1 1 0 0
S2 S3 I1 0 0 0 0
S2 S4 I2 0 0 0 0
S2 S0 I5 1 0 1 0
S3 S4 I1 0 0 0 0
S3 S0 I2 1 0 0 0
S3 S0 I5 1 1 1 0
S4 S0 I1 1 0 0 0
S4 S0 I2 1 1 0 0
S4 S0 I5 1 0 0 1




Quartus Prime



Quartus hat eine kostenlose Lite Edition, die im Vergleich zur Professional Edition einige Einschränkungen aufweist. Die Hauptbeschränkung beträgt nicht mehr als 10.000 Zeilen Quellcode für die Projektsimulation. Laden Sie es herunter, nach der Registrierung können Sie dem Link folgen. Zum Zeitpunkt des Schreibens war die neueste Version 19.1. Aufgrund der Arbeit mit dieser Version habe ich einen Artikel geschrieben. Wir wählen Lite Edition, Version 19.1, das Windows-Betriebssystem (es sollte beachtet werden, dass es eine Quartus-Version für Linux gibt und diese einwandfrei funktioniert. Probleme treten bei ModelSim auf, das 32-Bit ist und die alte Version der Font-Mapping-Bibliothek verwendet. Daher empfehle ich zunächst die Verwendung der Windows-Version ), wählen Sie die Registerkarte Kombinierte Dateien. Die Archivgröße für den Download ist sehr groß - 5,6 GB, denken Sie daran. Erweitern Sie das heruntergeladene Archiv und führen Sie es aussetup.bat . Die Installation erfolgt standardmäßig, wir verwenden die Standardauswahl der Komponenten.



Projekterstellung



Um ein neues Projekt zu erstellen, wählen Sie den Assistenten Datei -> Neu ... nach Projekt . Das erste Fenster des Assistenten dient nur zur Information. Klicken Sie auf Weiter . Im zweiten Fenster wählen wir aus, wo sich das Projekt befinden soll. Der Name lautet "soda_machine" und das übergeordnete Gestaltungselement lautet "soda_machine" , wie in der Abbildung dargestellt: Wählen Sie



Bild



im nächsten Fenster "Leeres Projekt" aus . Fenster Dateien hinzufügen "Dateien hinzufügen " , nichts hinzufügen. Das Fenster zur Auswahl des Geräts „Familien-, Geräte- und Karteneinstellungen“ ist für ein reales Projekt sehr wichtig. Da unser Projekt jedoch alles



Bild



andere als real ist, belassen wir hier die Standardeinstellungen wie in der Abbildung: Fenster zum Auswählen von Einstellungen für andere ToolsWählen Sie unter "EDA-Tool-Einstellungen" die Option aus , um das Projekt zu simulieren und "ModelSim-Altera" und das Format "System Verilog HDL" wie im Bild zu verwenden: Klicken Sie im



Bild



letzten Informationsfenster "Zusammenfassung" auf Fertig stellen .



Quellcode schreiben



Wir werden zwei Hauptdateien mit dem Quellcode haben, dies ist das soda_machine- Modul selbst und sein Prüfstand. Beide Dateien verwenden den Datentyp insert_type , der beschreibt, wie wir Münzbezeichnungen codieren, und es ist logisch, ihn in eine separate Datei zu trennen. Mit den Kompilierungsfunktionen von Quartus und ModelSim sind jedoch einige Schwierigkeiten verbunden. Quartus kompiliert alle Quelldateien in einem einzigen Durchgang, und ModelSim kompiliert jede Datei separat, um den von Quartus'om entstandenen Override-Typ insert_type zu kompilieren . Ich habe die Technik des C / C ++ Include Guard basierend auf den Anweisungen des Makroprozessors verwendet. Stellen Sie außerdem sicher, dass ModelSim den im Modul soda_machine verwendeten insert_type verwendetund auf dem Prüfstand platzierte derselbe seine Beschreibung im Paket soda_machine_types . Unter Berücksichtigung dieser Anforderungen sieht die Datei soda_machine_types.sv folgendermaßen aus:



soda_machine_types.sv
`ifndef soda_machine_types_sv_quard

package soda_machine_types;

	typedef enum logic [1:0] {I1=2'b01, I2=2'b10, I5=2'b11} insert_type;
	
endpackage

`define soda_machine_types_sv_quard
`endif




Jetzt befindet sich das soda_machine- Modul selbst in der Datei soda_machine.sv :



soda_machine.sv
`include "soda_machine_types.sv"
import soda_machine_types::*;


module soda_machine(
	input logic clk,          // Clock 
	input logic reset,        // Active high level
	input insert_type insert,
	output logic pour_water,
	output logic change1,
	output logic change2,
	output logic change22);

	typedef enum logic [2:0] {S0, S1, S2, S3, S4} state_type;
	(* syn_encoding = "default" *) state_type state, nextstate;
	//       
	always_ff @(posedge clk, posedge reset)
	if (reset)
		state <= S0;
	else
		state <= nextstate;
	//            
	always_comb
		case (state)
			S0:
				case (insert)
					I1:
						nextstate = S1;
					I2:
						nextstate = S2;
					I5:
						nextstate = S0;
				endcase
			S1: 
				case (insert)
					I1:
						nextstate = S2;
					I2:
						nextstate = S3;
					I5:
						nextstate = S0;
				endcase
			S2:
				case (insert)
					I1:
						nextstate = S3;
					I2: 
						nextstate = S4;
					I5:
						nextstate = S0;
				endcase
			S3: 
				if (insert == I1)
					nextstate = S4;
				else
					nextstate = S0;
			S4:
				nextstate = S0;
		endcase
	//    
	assign pour_water = (state == S4) | (insert == I5) | (state == S3) & (insert == I2);
	
	assign change1 = (state == S1) & (insert == I5) | (state == S3) & (insert == I5) | (state == S4) & (insert == I2);
							
	assign change2 = (state == S2) & (insert == I5) | (state == S3) & (insert == I5);
	
	assign change22 = (state == S4) & (insert == I5);
	
endmodule




Wie die Zustandsmaschinenzustände codiert sind, habe ich Quartus überlassen. Um anzugeben, wie genau die Codierung erfolgen soll, wird das Attribut (* syn_encoding = "default" *) verwendet . Weitere Codierungsoptionen finden Sie hier .



Es ist zu beachten, dass in einem realen Projekt die Ausgangssignale der Kombinationslogik des Mealy-Automaten in Registern gespeichert und bereits vom Ausgang der Register dem FPGA-Ausgang zugeführt werden müssen. Eingangssignale müssen mit Synchronisierern mit der Taktfrequenz synchronisiert werden, um nicht in einen metastabilen Zustand zu geraten.



Verwenden Sie zum Hinzufügen von Dateien zum Projekt Datei -> Neue "SystemVerilog HDL-Datei".und geben Sie beim Speichern den entsprechenden Namen an. Nach dem Hinzufügen dieser beiden Dateien kann das Projekt mit Verarbeitung -> Kompilierung starten kompiliert werden . Nach erfolgreicher Kompilierung sehen Sie das resultierende Schema Tools -> Netlist Viewer -> RTL Viewer :



RTL Viewer
image



So zeigen Sie das Statusdiagramm der Statusmaschine an Tools -> Netzlisten-Viewer -> Statusmaschinen-Viewer



State Machine Viewer
image



Auf der Registerkarte Codierung können Sie sehen, dass Quartus das "One-Hot" -Codierungsschema angewendet hat. Dies ist der Fall, wenn für jeden Zustand ein separates D-Flip-Flop verwendet wird und der S 0 -Zustand 0 und nicht 1 wie bei anderen Zuständen codiert wird. Dies wird durchgeführt, um die Rücksetzschaltung auf den Anfang zu vereinfachen Zustand. Möglicherweise stellen Sie fest, dass der RTL-Viewer kein sehr einfaches Diagramm anzeigt, sondern eher ein Konzept. Verwenden Sie zum Anzeigen des schematischen Diagramms Extras -> Netzlistenansichten -> Technologiekarten-Viewer (Nachanpassung).



Simulation



Im Prinzip haben wir im Moment ein Diagramm eines Automaten für den Verkauf von Sodawasser, aber wir müssen sicherstellen, dass es richtig funktioniert. Dazu schreiben wir einen Prüfstand und platzieren ihn in der Datei soda_machine_tb.sv :



soda_machine_tb.sv
`include "soda_machine_types.sv"
import soda_machine_types::*;

module soda_machine_tb;

	insert_type insert;
	
	logic [5:0] testvectors[10000:0];
	
	int vectornum, errors;
	
	logic clk, reset, pour_water, change1, change2, change22;
	logic pour_water_expected, change1_expected, change2_expected, change22_expected;
	//  
	soda_machine dut(
		.clk(clk),
		.reset(reset),
		.insert(insert),
		.pour_water(pour_water),
		.change1(change1),
		.change2(change2),
		.change22(change22)
	);
	//   
	always
		#5 clk = ~clk;
	//   
	initial begin
		//     
		$readmemb("../../soda_machine.tv", testvectors);
		vectornum = 0;
		errors = 0;
		clk = 1;
		//   
		reset = 1; #13; reset = 0;
	end
	//   
	always @(posedge clk) begin
		#1; {insert, pour_water_expected, change1_expected, change2_expected, change22_expected} = testvectors[vectornum];
	end
	// ,      
	always @(negedge clk)
		if (~reset) begin
			if ((pour_water !== pour_water_expected) || (change1 !== change1_expected) || (change2 !== change2_expected) ||
				(change22 !== change22_expected)) begin
				$error("%3d test insert=%b\noutputs pour_water=%b (%b expected), change1=%b (%b expected), change2=%b (%b expected), change22=%b (%b expected)", 
					vectornum + 1, insert, pour_water, pour_water_expected, change1, change1_expected, change2, change2_expected, change22, change22_expected);
				errors = errors + 1;
			end
			vectornum = vectornum + 1;
			if (testvectors[vectornum] === 6'bx) begin
				$display("Result: %3d tests completed with %3d errors", vectornum, errors);
				$stop;
			end
		end

endmodule




Zum Testen unseres Moduls verwenden wir die Testvektordatei soda_machine.tv :



soda_machine.tv
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
01_1_0_0_0
10_0_0_0_0
10_0_0_0_0
10_1_1_0_0
11_1_0_0_0
10_0_0_0_0
10_0_0_0_0
11_1_0_0_1
10_0_0_0_0
11_1_0_1_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
11_1_1_1_0




Die ersten beiden Bits sind das Einfügen des Eingangssignals, die nächsten 4 Bits sind unsere Erwartungen an die Ausgangssignale: pour_water, change1, change2, change22. Zum Beispiel wird am Anfang der Datei eine Rubelmünze fünfmal hintereinander eingefügt. Auf der fünften Münze warten wir, bis das pour_water-Signal erscheint, während die Änderungssignale inaktiv sind. Die Datei soda_machine.tv wird dem Projekt Datei -> Neue Textdatei hinzugefügt. Fügen



Sie zur Erleichterung der Arbeit mit ModelSim die Datei soda_machine_run_simulation.do mit den folgenden Inhalten hinzu:



soda_machine_run_simulation.do
add wave /soda_machine_tb/dut/clk
add wave /soda_machine_tb/dut/reset
add wave /soda_machine_tb/dut/insert
add wave /soda_machine_tb/dut/state
add wave /soda_machine_tb/dut/nextstate
add wave /soda_machine_tb/dut/pour_water
add wave /soda_machine_tb/dut/change1
add wave /soda_machine_tb/dut/change2
add wave /soda_machine_tb/dut/change22
view structure
view signals
run -all
wave zoom full




Es wird unsere Simulation ausführen und die Signaldiagramme an ModelSim ausgeben. Die Datei soda_machine_run_simulation.do wird der Projektdatei hinzugefügt -> Neue "Tcl-Skriptdatei"



Jetzt richten wir das Projekt so ein, dass die Simulation automatisch startet. Wählen Sie den Menüpunkt Zuordnungen -> Einstellungen , wählen Sie die Kategorie EDA-Werkzeugeinstellungen -> Simulation . In den NativeLink Einstellungen wählen Compile Prüfstand: und klicken Sie auf die Prüfstände Schaltfläche ... in dem Prüfstand Fenster öffnet klicken Sie auf die neue Schaltfläche ... in den neuen Prüfstand Einstellungen - Fenstern , das öffnet Füllung im Prüfstandsnamen: soda_machine_tb Feldund klicken Sie auf die Schaltfläche zur Dateiauswahl ... unten im Fenster, wählen Sie unsere Datei soda_machine_tb.sv aus und klicken Sie auf die Schaltfläche Hinzufügen . Es sollte folgendermaßen aussehen: Klicken Sie im



Bild



Fenster Neue Prüfstandseinstellungen auf OK . Das Fenster Test Benches sollte folgendermaßen aussehen: Klicken Sie im

Bild



Fenster Test Benches auf OK . Aktivieren Sie in den NativeLink-Einstellungen das Kontrollkästchen Skript zum Einrichten der Simulation verwenden und wählen Sie die Datei soda_machine_run_simulation.do aus . Das Fenster Einstellungen

sollte folgendermaßen aussehen: Klicken Sie



Bild



im Fenster Einstellungen aufOK , wir kompilieren das Projekt Verarbeitung -> Kompilierung starten , Simulationswerkzeuge ausführen -> Simulationswerkzeug ausführen -> RTL-Simulation . ModelSim sollte das Projekt starten und simulieren. Aussehen der Registerkarte "Transkript":



Registerkarte ModelSim Transcript
image



Die Ausgabe unseres Prüfstands zur Anzahl der durchgeführten Tests und erkannten Fehler ist rot hervorgehoben. Erscheinungsbild der Wave-Registerkarte:



Registerkarte ModelSim Wave
image



Projektquellcode



Der Quellcode des Projekts befindet sich in github.com/igoral5/soda_machine. Klonen Sie das Projekt und öffnen Sie das Projekt mit Quartus File -> Open Project ... Wählen Sie die

Datei soda_machine.qpf aus . Kompilieren Sie dann das Projekt Verarbeitung -> Kompilierung starten und starten Sie die Simulationswerkzeuge -> Simulationswerkzeug ausführen -> RTL-Simulation .



All Articles