关于状态机的基础知识可以访问以下链接学习:verilog中级语法之同步状态机原理和设计(十二)_ICer吼吼的博客-CSDN博客_同步状态机状态机
https://blog.csdn.net/weixin_45680021/article/details/125286469
补充:
一段式、二段式、三段式状态机区别:
一段式就是把状态,输入,输出都放在一个always里面;
二段式就是一个always语句来实现时序逻辑,另外一个always语句来实现组合逻辑,需要定义现态和次态两个状态(current_state, next_state);
三段式就是把二段式中组合逻辑的输出单独提出来再放一个always用非阻塞方式输出,有效去掉二段式中组合逻辑直接用阻塞方式输出存在的毛刺问题 。
一般建议使用三段式描述法
有限状态机(FSM):
• 状态机是绝大部分数字控制电路的核心结构。
• moore有限状态机:输出仅依赖于内部状态,跟输入无关。
• mealy有限状态机:输出不仅决定于内部状态,还跟外部输入有关。
• 有限状态机可以使用always语句和case语句描述,状态保存在寄存器中,根据寄存器不同的值(状态)执行不同的操作,case语句的多个分支则代表了不同状态的行为。


两段式描述:
- //二段式状态机设计
- module fsm1(ds,rd,go,ws,clk,rst_n);
- output ds,rd;
- input go,ws;
- input clk, rst_n;
- reg ds,rd;
-
- parameter [1:0] IDLE=2'b00,
- READ=2'b01,
- DLY=2'b10,
- DONE=2'b11;
-
- reg [1:0] state,next;
-
- always@(posedge clk or negedge rst_n); //定义初始状态
- if(!rst_n)
- state<=IDLE;
- else
- state<=next;
-
- always@(state or go or ws) begin //定义下一个状态和输出
- next=2'bx;
- ds=1'b0;
- rd=1'b0;
- case(state)
- IDLE:if(go)
- next=READ;
- else
- next=IDLE;
- READ: begin
- next=DLY;
- rd=1'b1;
- end
- DLY: begin
- rd=1'b1;
- if(ws)
- next=READ;
- else
- next=DONE;
- end
- DONE: begin
- ds=1'b1;
- next=IDLE;
- end
- endcase
- end
- endmodule
三段式描述:
- //三段式状态机设计
- module fsm1b(ds,rd,go,ws,clk,rst_n);
- output ds,rd;
- input go,ws;
- input clk,rst_n;
- reg ds,rd;
-
- parameter [1:0] IDLE=2'b00,
- READ=2'b01,
- DLY=2'b10,
- DONE=2'b11;
-
- reg [1:0] state,next;
-
- always@(posedge clk or negedge rst_n) //定义初始状态
- if(!rst_n)
- state<=IDLE;
- else
- state<=next;
-
- always@(state or go or ws) begin //状态转换
- next=2'bx;
- case(state)
- IDLE:
- if(go)
- next=READ;
- else
- next=IDLE;
- READ:
- next=DLY;
- DLY:
- if(ws)
- next=READ;
- else
- next=DONE;
- DONE:
- next=IDLE;
- endcase
- end
-
- always@(posedge clk or negedge rst_n)//输出
- if(!rst_n) begin
- ds<=1'b0;
- rd<=1'b0;
- end
- else begin
- ds<=1'b0;
- rd<=1'b0;
- end
- case(state)
- IDLE:
- if(go)
- rd<=1'b1;
- READ:
- rd<=1'b1;
- DLY:
- if(ws)
- rd<=1'b1;
- else
- ds<=1'b1;
- endcase
- end
- endmodule
要求:简化考虑,假设饮料只有一种价格为2.5元。 硬币有0.5元和1.0元两种,考虑找零,用Verilog描述其控制电路。
设计步骤分解:
1. 分析输入输出端口信号;
2. 状态转移图;
3. 根据状态转移图进行Verilog 语言描述;
4. 测试代码编写,仿真;
1. 输入输出端口信号分析
输入信号: clk,rst;
输入信号:操作开始: op_start; //定义1开始操作输入信号
投币币值: coin_val; //定义2’b01表示0.5元; 2’b10表示1元
输入信号:取消操作指示 : cancel_flag; //定义1为取消操作
输出信号:机器是否占用: hold_ind; //定义0为不占用,可以使用
输出信号:取饮料信号: drinktk_ind; //定义1为取饮料
输出信号:找零与退币标志信号: charge_ind; //定义1为找零
输出信号:找零与退币币值:charge_val; //定义3’b001表示找0.5元;3’b010表示找1元;3’b011表示找1.5元; 3’b100表示找2.0元;

说明:
1. 在S0状态下,如果检测到op_start=1,开始检测是否有投币,如果有,一次新的售货操作开始;
2. 在状态S1/S2/S3/S4下,如果检测到cancel_flag=1,则取消操作,状态返回S0,并退回相应的币值;
3. 在状态S5下,卖出饮料不找零;在状态S6下,卖出饮料并找零;
4. 在状态S5和S6 操作完后,都返回状态S0,等待下一轮新的操作开始;
5. 只有在S0 状态下,hold_ind=0,可以发起新一轮操作,其它状态下都为1;
RTL代码:
- module softdrinkFSM(clk,rst,op_start,cancel_flag,coin_val,hold_ind,charge_ind,drinktk_ind,charge_val);
- input clk,rst;
- input op_start;
- input cancel_flag;
- input [1:0] coin_val;
-
- output hold_ind,charge_ind,drinktk_ind;
- output [2:0] charge_val;
-
- reg hold_ind,charge_ind,drinktk_ind;
- reg [2:0] charge_val;
- reg [2:0] currentstate,nextstate;
-
- parameter S0=3'b000;
- parameter S1=3'b001;
- parameter S2=3'b010;
- parameter S3=3'b011;
- parameter S4=3'b100;
- parameter S5=3'b101;
- parameter S6=3'b110;
-
- always@(posedge clk or posedge rst)
- if(rst)
- currentstate<=0;
- else
- currentstate<=nextstate;
-
- always@(currentstate or rst or op_start or cancel_flag or coin_val)
- if(rst)
- nextstate=S0;
- else
- case(currentstate)
- S0:
- if(op_start)
- if(coin_val==2'b01)
- nextstate=S1;
- else if(coin_val==2'b10)
- nextstate=S2;
- S1:
- if(cancel_flag)
- nextstate=S0;
- else if(coin_val==2'b01)
- nextstate=S2;
- else if(coin_val==2'b10)
- nextstate=S3;
- S2:
- if(cancel_flag)
- nextstate=S0;
- else if(coin_val==2'b01)
- nextstate=S3;
- else if(coin_val==2'b10)
- nextstate=S4;
- S3:
- if(cancel_flag)
- nextstate=S0;
- else if(coin_val==2'b01)
- nextstate=S4;
- else if(coin_val==2'b10)
- nextstate=S5;
- S4:
- if(cancel_flag)
- nextstate=S0;
- else if(coin_val==2'b01)
- nextstate=S5;
- else if(coin_val==2'b10)
- nextstate=S6;
- S5:
- nextstate=S0;
- S6:
- nextstate=S0;
- default:
- nextstate=S0;
- endcase
-
- always@(currentstate)
- if(currentstate==S0)
- hold_ind=0;
- else
- hold_ind=1;
-
- always@(currentstate)
- if((currentstate==S5)||(currentstate==S6))
- drinktk_ind=1'b1;
- else
- drinktk_ind=1'b0;
-
- always@(currentstate or cancel_flag)
- if(currentstate==S0)
- charge_ind=1'b0;
- else if(currentstate==S6)
- charge_ind=1'b1;
- else if(cancel_flag)
- charge_ind=1'b1;
- else
- charge_ind=1'b0;
-
- always@(currentstate or cancel_flag)
- if(currentstate==S0)
- charge_val=3'b000;
- else if(currentstate==S6)
- charge_val=3'b001;
- else if(cancel_flag) begin
- case(currentstate)
- S1:
- charge_val=3'b001;
- S2:
- charge_val=3'b010;
- S3:
- charge_val=3'b011;
- S4:
- charge_val=3'b100;
- default:
- charge_val=3'b000;
- endcase
- end
- else
- charge_val=3'b000;
- endmodule
TB代码:
- //softdrinkFSM_td
- module softdrinkFSM_td;
- reg clk,rst;
- reg op_start;
- reg cancel_flag;
- reg [1:0] coin_val;
- wire hold_ind,charge_ind,drinktk_ind;
- wire [2:0] charge_val;
-
- initial begin
- clk=0;
- end
-
- always
- #500 clk=~clk;
-
- initial begin
- rst=0;
- op_start=0;
- cancel_flag=0;
- coin_val=2'b00;
- #25 rst=1;
- #25 rst=0;
-
- //第一次:依次投入一元,一元,一元的硬币
- #50 op_start=1;
- #300 coin_val=2'b10;
- #1000 coin_val=2'b10;
- #1000 coin_val=2'b10;
- #1000 op_start=0;
-
- //第二次:依次投入0.5元,一元,一元的硬币
- #2000 op_start=1;
- #300 coin_val=2'b01;
- #1000 coin_val=2'b10;
- #1000 coin_val=2'b10;
- #1000 op_start=0;
-
- //第三次:依次投入0.5元,一元的硬币,然后取消操作
- #2000 op_start=1;
- #300 coin_val=2'b01;
- #1000 coin_val=2'b10;
- #1000 cancel_flag=1'b1;
- op_start=0;
- #1000 cancel_flag=1'b0;
-
- //第四次:依次投入0.5元,0.5元,0.5元,0.5元,一元的硬币,然后取消操作
- #2000 op_start=1;
- #300 coin_val=2'b01;
- #1000 coin_val=2'b01;
- #1000 coin_val=2'b01;
- #1000 coin_val=2'b01;
- #1000 coin_val=2'b10;
- #1000 op_start=0;
- #1000000 $stop;
- end
-
- softdrinkFSM init(clk,rst,op_start,cancel_flag,coin_val,hold_ind,charge_ind,drinktk_ind,charge_val);
-
- endmodule
波形:
第一次:

第二次:

第三次:

第四次:
