Formulation du problème
Vous avez été convaincu de concevoir une machine à boissons gazeuses pour le bureau. Le coût des boissons est partiellement couvert par le syndicat, ils ne coûtent donc que 5 roubles. La machine accepte des pièces de 1, 2 et 5 roubles. Dès que l'acheteur fait la quantité nécessaire, la machine distribue une boisson et remet la monnaie. Concevez un distributeur automatique de boissons gazeuses. Les entrées de la machine sont de 1, 2 et 5 roubles, à savoir laquelle de ces pièces est insérée.
Supposons que pour chaque signal d'horloge, une seule pièce soit insérée. La machine a des sorties: pour soude, retour 1 rouble, retour 2 roubles, retour 2 à 2 roubles. Dès que 5 roubles (ou plus) sont collectés dans le distributeur automatique, il définit le signal "FILL GASING", ainsi que des signaux pour le retour du changement correspondant. La machine doit alors être à nouveau prête à accepter des pièces.
Théorie
Les machines à états finis ou machines à états finis (FSM) appartiennent à la classe des circuits séquentiels synchrones qui représentent la grande majorité des circuits numériques. C'est ainsi que vous devez mettre en œuvre vos projets (du moins au début). Cette méthode assure la répétabilité et la vérification du circuit et est indépendante des relations de retard des différents éléments du circuit. Les règles de construction de circuits séquentiels synchrones indiquent qu'un circuit est un circuit séquentiel synchrone si ses éléments remplissent les conditions suivantes:
- Chaque élément de circuit est soit un registre, soit un circuit combinatoire.
- Au moins un élément de schéma est un registre.
- Tous les registres sont cadencés avec un seul signal d'horloge.
- Chaque chemin cyclique contient au moins un registre.
Une machine à états a plusieurs états, qu'elle stocke dans des registres. Lorsqu'un signal d'horloge arrive, la machine à états peut changer son état, et la manière exacte dont l'état change dépend des signaux d'entrée et de l'état actuel. Dans le cas le plus simple, il peut ne pas y avoir de signaux d'entrée du tout, donc le diviseur de fréquence fonctionne. Il existe deux classes principales de machines à états finis: un automate Moore, dans lequel les signaux de sortie dépendent uniquement de l'état actuel de l'automate, et un automate Mealy, dans lequel les signaux de sortie dépendent de l'état actuel et des signaux d'entrée. En principe, toute machine à états finis peut être implémentée à la fois selon le schéma de Moore et le schéma de Miley, la différence entre eux sera que l'automate de Moore aura plus d'états et il sera une horloge derrière l'automate de Mily.Pour le circuit de la machine à soda, j'utiliserai le circuit Miles. Disons la machine d'état:
symbole | La description |
---|---|
S 0 | L'état initial, le montant cumulé de 0 roubles. |
S 1 | La quantité accumulée est de 1 frottement. |
S 2 | Accumulé 2 roubles. |
S 3 | Accumulé 3 roubles. |
S 4 | Accumulé 4 roubles. |
En tant que signal d'entrée, un bus à deux bits agira, avec le codage suivant de la valeur faciale de la pièce:
symbole | Valeur | La description |
---|---|---|
Je 1 | 01 | 1 frotter |
Je 2 | Dix | RUB 2 |
Je 5 | Onze | 5 RUB |
Dessinons un diagramme d'états de notre automate (sur les diagrammes d'états de l'automate Mealy, il faut indiquer les signaux de sortie sur les flèches de transition d'état, je ne le ferai pas, pour ne pas encombrer le diagramme, tous les signaux de sortie seront décrits dans le tableau ci-dessous):
Écrivons le tableau des changements d'état et des signaux de sortie:
États | Signaux d'entrée | |||||
---|---|---|---|---|---|---|
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 a une édition Lite gratuite, qui a quelques limitations par rapport à l'édition professionnelle, la principale limitation n'est pas plus de 10 000 lignes de code source pour simuler un projet. Téléchargez-le, après l'enregistrement, vous pouvez suivre le lien , au moment d'écrire la dernière version était 19.1, basée sur le travail avec cette version, j'ai écrit un article. Nous choisissons Lite Edition, version 19.1, système d'exploitation Windows (il convient de noter qu'il existe une version de Quartus pour Linux et que cela fonctionne correctement, des problèmes surviennent avec ModelSim, qui est de 32 bits et utilise une ancienne version de la bibliothèque d'affichage des polices, donc au début je recommande d'utiliser la version Windows ), sélectionnez l'onglet Fichiers combinés. La taille de l'archive pour le téléchargement est assez grande - 5,6 Go, gardez cela à l'esprit. Développez l'archive téléchargée et exécutezsetup.bat . L'installation se fait de manière standard, nous utilisons la sélection par défaut des composants.
Création de projet
Pour créer un nouveau projet, sélectionnez l'Assistant Fichier -> Nouveau ... par projet . La première fenêtre de l'assistant est informative, cliquez sur Suivant , dans la deuxième fenêtre, sélectionnez l'emplacement du projet, son nom "soda_machine" et l'élément de conception de niveau supérieur "soda_machine" , comme dans l'image:
Dans la fenêtre suivante, sélectionnez "Projet vide" . La fenêtre d'ajout de fichiers "Ajouter des fichiers" , n'ajoutez rien. La fenêtre de sélection des appareils «Famille, Périphériques et Carte» est très importante pour un vrai projet, mais comme notre projet est éducatif et qu'il est loin d'être réel, nous laissons les paramètres par défaut ici, comme dans la figure:
Fenêtre de sélection des paramètres pour d'autres outils«EDA Tool Settings» , nous choisissons d'utiliser «ModelSim-Altera» et le format «System Verilog HDL» pour simuler le projet comme sur la figure:
Dernière fenêtre d'information «Résumé» , cliquez sur Terminer .
Écrire du code source
Nous aurons deux fichiers principaux avec le code source, il s'agit du module soda_machine lui - même et de son banc de test, ces deux fichiers utiliseront le type de données insert_type , qui décrit comment nous encodons les dénominations des pièces et il est logique de le séparer dans un fichier séparé. Mais il y a quelques difficultés associées aux fonctionnalités de compilation de Quartus et ModelSim. Quartus compile tous les fichiers source en un seul passage, et ModelSim compile chaque fichier séparément, de sorte que lors de la compilation de Quartus, il n'y aurait pas de redéfinition du type insert_type , j'ai utilisé la technique de C / C ++ include guard basée sur les directives du microprocesseur. De plus, ce ModelSim serait sûr que le type insert_type utilisé dans le module soda_machineet dans le banc de test, un seul et même, mettre sa description dans le paquet soda_machine_types . Avec ces exigences à l' esprit, le fichier soda_machine_types.sv ressemble à ceci:
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
Maintenant, le module soda_machine lui - même se trouve dans le fichier 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
Comment les états de la machine d'état sont encodés, je l'ai laissé à Quartus. Afin d'indiquer comment exactement l'encodage doit être fait, l'attribut (* syn_encoding = "default" *) est utilisé , d'autres options d'encodage peuvent être vues ici .
Il convient de noter que dans un projet réel, les signaux de sortie de la logique combinatoire de la machine Miles doivent être stockés dans des registres et transmis de la sortie des registres à la sortie FPGA. Les signaux d'entrée doivent être synchronisés avec la fréquence d'horloge à l'aide de synchroniseurs afin d'éviter de tomber dans un état métastable.
Pour ajouter des fichiers au projet, utilisez Fichier -> Nouveau "Fichier HDL SystemVerilog"et donnez un nom approprié lors de l'enregistrement. Après avoir ajouté ces deux fichiers, le projet peut être compilé Traitement -> Démarrer la compilation . Après une compilation réussie, vous pouvez voir les outils de schéma résultants -> Visionneuses de listes de favoris -> Visionneuse RTL :
Visionneuse RTL
Pour afficher le diagramme d'état de la machine à états Outils -> Visionneuses de netlistes -> Visionneuse de machines à états
Visualiseur de machine d'état
Sur l'onglet Encodage, vous pouvez voir que Quartus a appliqué le schéma de codage "à chaud", c'est quand une bascule D séparée est utilisée pour chaque état, et l'état S 0 est codé 0, et non 1 comme pour les autres états, cela est fait pour simplifier le circuit de réinitialisation à l'initial Etat. Vous remarquerez peut-être que la visionneuse RTL n'affiche pas exactement un diagramme schématique, mais plutôt un concept. Pour afficher le diagramme schématique, utilisez Outils -> Netlist Viewrs -> Technology Map Viewer (post-ajustement)
Simulation
En principe, pour le moment, nous avons un circuit pour une machine de vente d'eau gazeuse, mais vous devez vous assurer qu'il fonctionne correctement, pour cela, nous écrivons un banc de test et le plaçons dans le fichier 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
Pour tester notre module, nous utilisons le fichier vectoriel de test 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
Les deux premiers bits sont l'entrée d'insertion, les 4 bits suivants sont nos attentes pour les sorties: pour_water, change1, change2, change22. Par exemple, au début du fichier, une pièce en rouble est insérée 5 fois de suite, sur la cinquième pièce, on attend que le signal pour_water apparaisse, alors que les signaux de changement sont inactifs. Le fichier soda_machine.tv est ajouté au projet Fichier -> Nouveau «fichier texte».
Pour plus de facilité avec ModelSim, ajoutez le fichier soda_machine_run_simulation.do avec le contenu suivant:
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
Il exécutera notre simulation et affichera les formes d'onde dans ModelSim. Le fichier soda_machine_run_simulation.do est ajouté au fichier -> Nouveau projet «Fichier de script Tcl»
Nous allons maintenant configurer le projet pour que la simulation démarre automatiquement. Sélectionnez l'élément de menu Affectations -> Paramètres , sélectionnez la catégorie Paramètres de l'outil EDA -> Simulation . Dans les paramètres NativeLink, sélectionnez Compiler le banc de test: et cliquez sur le bouton Bancs de test ... dans la fenêtre Bancs de test qui s'ouvre, cliquez sur le bouton Nouveau ... Dans la fenêtre Paramètres du nouveau banc de test qui s'ouvre, renseignez le champ Nom du banc de test: soda_machine_tbet cliquez sur le bouton de sélection de fichier ... en bas de la fenêtre, sélectionnez notre fichier soda_machine_tb.sv et cliquez sur le bouton Ajouter . Cela devrait ressembler à la figure:
Dans la fenêtre Paramètres du nouveau banc de test , cliquez sur OK . La fenêtre Bancs de test doit ressembler à ceci:
Dans la fenêtre Bancs de test , cliquez sur OK . Dans les paramètres NativeLink, définissez le script utilise pour mettre en place la simulation case et sélectionnez le soda_machine_run_simulation.do fichier . La fenêtre Paramètres
doit ressembler à ceci:
Dans la fenêtre Paramètres , cliquez surOK , nous compilons le projet Processing -> Start Compilation , exécutons les outils de simulation -> Run Simulation Tool -> RTL Simulation . ModelSim doit lancer et simuler le projet. Apparence de l'onglet Transcription:
Onglet Transcription ModelSim
Le cadre rouge marque la sortie de notre banc d'essai sur le nombre de tests effectués et les erreurs trouvées. Apparence de l'onglet Vague:
Onglet ModelSim Wave
Code source du projet
Le code source du projet est sur github.com/igoral5/soda_machine Clonez le projet, puis ouvrez le projet en utilisant Quartus File -> Open Project ...
sélectionnez le fichier soda_machine.qpf . Puis compilez le projet Processing -> Start Compilation et exécutez les outils de simulation -> Run Simulation Tool -> RTL Simulation .