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):
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
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
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
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
So zeigen Sie das Statusdiagramm der Statusmaschine an Tools -> Netzlisten-Viewer -> Statusmaschinen-Viewer
State Machine Viewer
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
Fenster Neue Prüfstandseinstellungen auf OK . Das Fenster Test Benches sollte folgendermaßen aussehen: Klicken Sie im
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
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
Die Ausgabe unseres Prüfstands zur Anzahl der durchgeführten Tests und erkannten Fehler ist rot hervorgehoben. Erscheinungsbild der Wave-Registerkarte:
Registerkarte ModelSim Wave
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 .