Mots-clés : machine d'état, distributeur automatique
La machine à états finis (FSM, Finite-State Machine), appelée machine d'état, est un modèle mathématique qui représente un nombre fini d'états et les comportements tels que les transitions et les actions entre ces états. La machine d'état n'est pas seulement un outil de description de circuit, mais aussi un mode de pensée, et elle est largement utilisée au niveau système et au niveau RTL dans la conception de circuits.
Types de machines d'état
En Verilog, les machines d'état sont principalement utilisées dans la conception de logique séquentielle synchrone, et peuvent faire basculer les états des circuits séquentiels entre un nombre fini d'états selon certaines exigences et règles. La direction du changement d'état dépend non seulement des valeurs d'entrée, mais aussi de l'état courant. Les machines d'état peuvent être divisées en 2 catégories : les machines d'état de Moore et les machines d'état de Mealy.
Machine d'état de type Moore
La sortie d'une machine d'état de Moore est liée uniquement à l'état courant, et non à l'entrée courante.
La sortie reste stable pendant un cycle d'horloge complet. Même si le signal d'entrée change à ce moment-là, la sortie ne changera pas. L'influence de l'entrée sur la sortie ne peut se refléter que dans le cycle d'horloge suivant. C'est également une caractéristique importante de la machine d'état de Moore : l'entrée et la sortie sont isolées.

Machine d'état de type Mealy
La sortie d'une machine d'état de Mealy dépend non seulement de l'état courant, mais aussi du signal d'entrée courant.
La sortie d'une machine d'état de Mealy change immédiatement après le changement du signal d'entrée, et le changement d'entrée peut se produire pendant le cycle d'horloge de n'importe quel état. Par conséquent, avec la même logique, la réponse de la sortie de la machine d'état de Mealy à l'entrée est en avance d'un cycle d'horloge par rapport à celle de la machine d'état de Moore.

Flot de conception d'une machine d'état
Dessinez le diagramme de transition d'état selon les exigences de conception, déterminez le type de machine à états à utiliser et étiquetez les divers signaux d'entrée et de sortie, ce qui est plus utile pour la programmation. Généralement, la plus couramment utilisée est la machine à états de type Mealy à 3 segments. Ce qui suit utilise un exemple spécifique de conception d'un distributeur automatique de boissons pour illustrer le processus de conception de la machine à états.
Distributeur automatique de boissons
La description fonctionnelle du distributeur automatique de boissons est la suivante :
Le prix de la boisson est de 2 yuan. Le distributeur automatique ne peut accepter que des pièces de 0.5 yuan et de 1 yuan. Tenir compte de la monnaie et de la distribution du produit. Les processus d'insertion de pièces et de distribution de produit sont effectués un à la fois ; il n'y aura pas de situation où plusieurs pièces sont insérées en même temps ou plusieurs bouteilles de boisson sont distribuées en même temps. Ce n'est qu'après que chaque cycle du distributeur automatique ait terminé l'insertion de pièces, la distribution du produit et le rendu de monnaie qu'il peut entrer dans un nouvel état de vente automatique.
Le diagramme de transition d'état de fonctionnement du distributeur automatique est présenté ci-dessous, incluant les états des signaux d'entrée et de sortie.
Ici, coin = 1 signifie qu'une pièce de 0.5 yuan est insérée, et coin = 2 signifie qu'une pièce de 1 yuan est insérée.

Conception de la machine à états : style à 3 segments (recommandé)
La conception de la machine à états est la suivante :
- (0) Tout d'abord, déterminez l'encodage de la machine à états en fonction du nombre d'états. L'utilisation de l'encodage pour attribuer des valeurs aux registres d'état améliore la lisibilité du code.
- (1) Le premier segment de la machine à états : logique séquentielle, affectation non bloquante, pour transférer l'état du registre.
- (2) Le deuxième segment de la machine à états : logique combinatoire, affectation bloquante, pour déterminer l'état suivant de la machine à états en fonction de l'état actuel et de l'entrée actuelle.
- (3) Le troisième segment de la machine à états : logique séquentielle, affectation non bloquante. Comme il s'agit d'une machine à états de Mealy, déterminez le signal de sortie en fonction de l'état actuel et de l'entrée actuelle.
Exemple
// 2 yuan pour une bouteille de boisson
// seulement 2 pièces acceptées : 5 jiao et 1 yuan
// terminer la fonction de vente et de rendu de monnaie
module vending_machine_p3 (
input clk ,
input rstn ,
input [1:0] coin , //01 pour 0.5 jiao, 10 pour 1 yuan
output [1:0] change ,
output sell //sortir la boisson
);
//décodage de l'état de la machine
parameter IDLE = 3'd0 ;
parameter GET05 = 3'd1 ;
parameter GET10 = 3'd2 ;
parameter GET15 = 3'd3 ;
//variable de machine
reg [2:0] st_next ;
reg [2:0] st_cur ;
//(1) transfert d'état
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= 'b0 ;
end
else begin
st_cur <= st_next ;
end
end
//(2) commutation d'état, en utilisant une affectation bloquante pour la logique combinatoire
//tous les éléments du case doivent être affichés complètement
always @(*) begin
//st_next = st_cur ;//Si les options de condition ne sont pas toutes prises en compte, vous pouvez affecter une valeur initiale pour éliminer le latch
case(st_cur)
IDLE:
case (coin)
2'b01: st_next = GET05 ;
2'b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase
GET05:
case (coin)
2'b01: st_next = GET10 ;
2'b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase
GET10:
case (coin)
2'b01: st_next = GET15 ;
2'b10: st_next = IDLE ;
default: st_next = GET10 ;
endcase
GET15:
case (coin)
2'b01,2'b10:
st_next = IDLE ;
default: st_next = GET15 ;
endcase
default: st_next = IDLE ;
endcase
end
//(3) logique de sortie, utilisant une affectation non bloquante
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
end
else if ((st_cur == GET15 && coin ==2'h1)
|| (st_cur == GET10 && coin ==2'd2)) begin
change_r <= 2'b0 ;
sell_r <= 1'b1 ;
end
else if (st_cur == GET15 && coin == 2'h2) begin
change_r <= 2'b1 ;
sell_r <= 1'b1 ;
end
else begin
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
end
end
assign sell = sell_r ;
assign change = change_r ;
endmodule
La conception du banc de test est la suivante. Quatre scénarios sont simulés dans la simulation, à savoir :
case1 correspond à l'insertion de quatre pièces de 0.5 yuan successivement ; case2 correspond à l'ordre d'insertion des pièces de 1 yuan - 0.5 yuan - 1 yuan ; case3 correspond à l'ordre de 0.5 yuan - 1 yuan - 0.5 yuan ; case4 correspond à l'ordre de trois pièces de 0.5 yuan consécutives suivies d'une pièce de 1 yuan.
Exemple
module test ;
reg clk;
reg rstn ;
reg [1:0] coin ;
wire [1:0] change ;
wire sell ;
//génération d'horloge
parameter CYCLE_200MHz = 10 ; //
always begin
clk = 0 ; #(CYCLE_200MHz/2) ;
clk = 1 ; #(CYCLE_200MHz/2) ;
end
//génération de motivation
reg [9:0] buy_oper ; //stocke l'état de l'opération d'achat
initial begin
buy_oper = 'h0 ;
coin = 2'h0 ;
rstn = 1'b0 ;
#8 rstn = 1'b1 ;
@(negedge clk) ;
//cas(1) 0.5 -> 0.5 -> 0.5 -> 0.5
#16 ;
buy_oper = 10'b00_0101_0101 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(2) 1 -> 0.5 -> 1, rendu de monnaie
#16 ;
buy_oper = 10'b00_0010_0110 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(3) 0.5 -> 1 -> 0.5
#16 ;
buy_oper = 10'b00_0001_1001 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
//case(4) 0.5 -> 0.5 -> 0.5 -> 1, rendu de monnaie
#16 ;
buy_oper = 10'b00_1001_0101 ;
repeat(5) begin
@(negedge clk) ;
coin = buy_oper[1:0] ;
buy_oper = buy_oper >> 2 ;
end
end
//(1) état de Mealy à 3 étages
vending_machine_p3 u_mealy_p3 (
.clk (clk),
.rstn (rstn),
.coin (coin),
.change (change),
.sell (sell)
);
//fin de simulation
always begin
#100;
if ($time >= 10000) $finish ;
end
endmodule // test
Les résultats de simulation sont les suivants :
On peut voir sur la figure que le signal sell, qui représente l'action de distribution du produit, peut normalement être mis à l'état haut une fois l'insertion de pièces terminée, et que le signal change, qui représente l'action de rendu de monnaie, peut également émettre le signal correct de rendu de monnaie ou d'absence de rendu de monnaie selon le scénario de pièces en entrée.
Modification de la machine d'état : style à 2 segments
Fusionnez les descriptions des segments 2 et 3 de la machine d'état à 3 segments, conservez les autres parties inchangées, et la machine d'état devient une description à 2 segments.
Les parties modifiées sont les suivantes :
Exemple
//tout en utilisant l'affectation bloquante pour la logique combinatoire
reg [1:0] change_r ;
reg sell_r ;
always @(*) begin //tous les éléments de case doivent être affichés complètement
case(st_cur)
IDLE: begin
change_r = 2'b0 ;
sell_r = 1'b0 ;
case (coin)
2'b01: st_next = GET05 ;
2'b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase // case (coin)
end
GET05: begin
change_r = 2'b0 ;
sell_r = 1'b0 ;
case (coin)
2'b01: st_next = GET10 ;
2'b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase // case (coin)
end
GET10:
case (coin)
2'b01: begin
st_next = GET15 ;
change_r = 2'b0 ;
sell_r = 1'b0 ;
end
2'b10: begin
st_next = IDLE ;
change_r = 2'b0 ;
sell_r = 1'b1 ;
end
default: begin
st_next = GET10 ;
change_r = 2'b0 ;
sell_r = 1'b0 ;
end
endcase // case (coin)
GET15:
case (coin)
2'b01: begin
st_next = IDLE ;
change_r = 2'b0 ;
sell_r = 1'b1 ;
end
2'b10: begin
st_next = IDLE ;
change_r = 2'b1 ;
sell_r = 1'b1 ;
end
default: begin
st_next = GET15 ;
change_r = 2'b0 ;
sell_r = 1'b0 ;
end
endcase
default: begin
st_next = IDLE ;
change_r = 2'b0 ;
sell_r = 1'b0 ;
end
endcase
end
Instanciez le module nouvellement modifié ci-dessus dans le banc de test à 3 segments pour effectuer la simulation. Les résultats sont les suivants :
On peut voir sur la figure que le signal de distribution de produit sell et le signal de monnaie change sont en avance d'un cycle d'horloge par rapport à la sortie de la machine à états à 3 segments. Cela est dû au fait que les signaux de sortie sont tous des affectations bloquantes.
Comme montré dans les parties entourées en rouge sur la figure, des impulsions parasites apparaissent sur les signaux de sortie. Cela est dû au fait que les signaux d'entrée sont asynchrones, et que les signaux de sortie sont des sorties de logique combinatoire sans pilotage d'horloge.
En pratique, si les signaux d'entrée sont synchronisés avec l'horloge, de telles impulsions parasites n'apparaîtront pas. Si les signaux d'entrée sont asynchrones, les signaux doivent d'abord être synchronisés.
Modification de la machine d'état : style à 1 segment (à utiliser avec prudence)
Fusionnez les descriptions des segments 1, 2 et 3 de la machine d'état à 3 segments, et la machine d'état devient une description à 1 segment.
Les parties modifiées sont les suivantes :
Exemple
//(1) en utilisant une variable d'état pour décrire
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= 'b0 ;
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
end
else begin
case(st_cur)
IDLE: begin
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
case (coin)
2'b01: st_cur <= GET05 ;
2'b10: st_cur <= GET10 ;
endcase
end
GET05: begin
case (coin)
2'b01: st_cur <= GET10 ;
2'b10: st_cur <= GET15 ;
endcase
end
GET10:
case (coin)
2'b01: st_cur <= GET15 ;
2'b10: begin
st_cur <= IDLE ;
sell_r <= 1'b1 ;
end
endcase
GET15:
case (coin)
2'b01: begin
st_cur <= IDLE ;
sell_r <= 1'b1 ;
end
2'b10: begin
st_cur <= IDLE ;
change_r <= 2'b1 ;
sell_r <= 1'b1 ;
end
endcase
default: begin
st_cur <= IDLE ;
end
endcase // case (st_cur)
end // else: !if(!rstn)
end
Instanciez le module nouvellement modifié ci-dessus dans le banc de test à 3 segments pour effectuer la simulation. Les résultats sont les suivants :
On peut voir sur la figure que les signaux de sortie sont exactement les mêmes que ceux de la machine d'état à 3 segments.
L'inconvénient de la machine d'état à 1 segment est que de nombreux types de logique sont mélangés, ce qui rend la maintenance ultérieure difficile. Lorsque la machine d'état et les signaux de sortie sont peu nombreux, ce style de description peut être essayé.
Modification de la machine d'état : type Moore
Si une machine d'état de Moore est utilisée pour décrire le flux de fonctionnement du distributeur automatique, alors 2 encodages d'état supplémentaires doivent être ajoutés pour décrire le signal d'entrée et l'état de la machine d'état lorsque la machine d'état de Mealy produit des sorties.
Le code Verilog du distributeur automatique décrit par la machine d'état de Moore à 3 segments est le suivant :
Exemple
input clk ,
input rstn ,
input [1:0] coin , //01 pour 0.5 jiao, 10 pour 1 yuan
output [1:0] change ,
output sell //sortir la boisson
);
//décodage de l'état de la machine
parameter IDLE = 3'd0 ;
parameter GET05 = 3'd1 ;
parameter GET10 = 3'd2 ;
parameter GET15 = 3'd3 ;
// nouvel état pour la machine d'état de Moore
parameter GET20 = 3'd4 ;
parameter GET25 = 3'd5 ;
//variable de machine
reg [2:0] st_next ;
reg [2:0] st_cur ;
//(1) transfert d'état
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
st_cur <= 'b0 ;
end
else begin
st_cur <= st_next ;
end
end
//(2) commutation d'état, en utilisant une affectation bloquante pour la logique combinatoire
always @(*) begin //tous les éléments de case doivent être affichés complètement
case(st_cur)
IDLE:
case (coin)
2'b01: st_next = GET05 ;
2'b10: st_next = GET10 ;
default: st_next = IDLE ;
endcase
GET05:
case (coin)
2'b01: st_next = GET10 ;
2'b10: st_next = GET15 ;
default: st_next = GET05 ;
endcase
GET10:
case (coin)
2'b01: st_next = GET15 ;
2'b10: st_next = GET20 ;
default: st_next = GET10 ;
endcase
GET15:
case (coin)
2'b01: st_next = GET20 ;
2'b10: st_next = GET25 ;
default: st_next = GET15 ;
endcase
GET20: st_next = IDLE ;
GET25: st_next = IDLE ;
default: st_next = IDLE ;
endcase // case (st_cur)
end // always @ (*)
// (3) logique de sortie,
// un cycle de retard lors de l'utilisation d'une assignation non bloquante
reg [1:0] change_r ;
reg sell_r ;
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
end
else if (st_cur == GET20 ) begin
sell_r <= 1'b1 ;
end
else if (st_cur == GET25) begin
change_r <= 2'b1 ;
sell_r <= 1'b1 ;
end
else begin
change_r <= 2'b0 ;
sell_r <= 1'b0 ;
end
end
assign sell = sell_r ;
assign change = change_r ;
endmodule
Instanciez la machine d'état Moore modifiée ci-dessus dans le banc de test à 3 segments pour effectuer la simulation. Les résultats sont les suivants :
On peut voir sur la figure que le signal de sortie est retardé d'un cycle d'horloge par rapport à la machine d'état de type Mealy à 3 segments. Cela est dû au fait que l'entrée dans la machine d'état d'encodage nouvellement ajoutée nécessite un retard d'un cycle d'horloge. À ce stade, l'utilisation d'une assignation non bloquante pour la sortie entraînera un retard du signal de sortie final d'un cycle d'horloge. C'est également une caractéristique de la machine d'état Moore.
Lors de l'assignation du signal de sortie, l'utilisation d'une assignation bloquante peut l'avancer d'un cycle d'horloge.
La logique de sortie est modifiée comme suit.
Exemple
// (3.2) logique de sortie, utilisant une assignation bloquante
reg [1:0] change_r ;
reg sell_r ;
always @(*) begin
change_r = 'b0 ;
sell_r = 'b0 ; //ne liste pas toutes les conditions, les initialise
if (st_cur == GET20 ) begin
sell_r = 1'b1 ;
end
else if (st_cur == GET25) begin
change_r = 2'b1 ;
sell_r = 1'b1 ;
end
end
Les résultats de simulation de l'assignation bloquante pour le signal de sortie sont les suivants :
On peut voir sur la figure que le signal de sortie est maintenant cohérent avec la machine d'état Mealy à 3 segments.
Téléchargement du code source
Téléchargement


