同步指状态机与系统时钟同步,有限指状态是有限个状态
两类状态机共同点是状态跳转只和输入有关,若输出只和状态有关与输入无关为Moore型,
若输出与状态和输入均有关为Mealy型。
状态机的具体理论可以参考“信息论”中的部分内容
状态机工作过程:0(条件)/0(结果)
可乐贩卖机系统
输入:投入1元硬币
输出:出可乐,不出可乐
状态:投入0元,投入1元,投入2元,投入3元
条件为是否投入硬币,结果为是否出可乐,显然不利用three的状态是可以完成任务的
此为Mealy状态机(在最后吐可乐时与输入条件有关了)
当然我们也可以加上three这个状态绘图如下,但是意义不大,对板上资源有所消耗,不提倡
此为Moore状态机
参照其上第一个Mealy型绘制波形图:
module simple_fsm ( input wire sys_clk, input wire res_n, input wire po_money, output reg po_cola ); // 一般0~4个状态采用二进制码,4~24个采用1位热码,24个以上采用格雷码编码。 parameter IDLE = 3'b001; //采用1位热码的形式表示状态,当然也可以采用二进制编码或者格雷码 parameter ONE = 3'b010; //读热码在综合时会简化比较器的位数,但是在存储上会占用3个位宽 parameter TWO = 3'b100; //FPGA中组合逻辑资源较多,综合布线的资源相对较少。在状态较多的情况下采用格雷码对资源的利用比较均衡 reg [2:0] state; // 一段式状态机:使用时序逻辑即描述状态的转移也描述状态的输出 // 二段式状态机:第一段状态机描述状态的转移,第二段描述逻辑的输出 // 三段式状态机:第一段采用时序逻辑描述状态转移,第二段采用组合逻辑判断状态转移条件描述状态转移规律,第三段描述状态输出可以使用时序或组合逻辑 //新二段式:将原三段式中第一二段合并为一段,第三段作为一段 always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) state <= IDLE; else case(state) IDLE:if(po_money == 1'b1) state <= ONE; else state <= IDLE; ONE:if(po_money == 1'b1) state <= TWO; else state <= ONE; TWO:if(po_money == 1'b1) state <= IDLE; else state <= TWO; default:state <= IDLE; endcase always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) po_cola <= 1'b0; else if((state == TWO)&&(po_money == 1'b1)) po_cola <= 1'b1; else po_cola <= 1'b0; endmodule
`timescale 1ns/1ns module tb_simple_fsm(); reg sys_clk; reg res_n; reg po_money; wire po_cola; initial begin sys_clk = 1'b0; res_n <= 1'b0; #30 res_n <= 1'b1; end always #10 sys_clk = ~sys_clk; always@(posedge sys_clk or negedge res_n) if(res_n == 1'b0) po_money <= 1'b0; else po_money <= {$random} % 2; wire [2:0] state = siple_fsm_1.state; //将模块实例化后内部的变量调用出来方便进行内部的状态监测 initial begin $timeformat(-9,0,"ns",6); $monitor("@time %t: po_money = %b,state = %b,po_cola = %b",$time,po_money,state,po_cola); end simple_fsm siple_fsm_1 ( .sys_clk(sys_clk), .res_n(res_n), .po_money(po_money), .po_cola(po_cola) ); endmodule
以上为简单的状态机组成
这里再次设计为当有零钱输入时可乐状态机的情况,即有不足一元硬币或需要找零的情况下可乐状态机的设计
共有7种情况
设置编码为 输入00:没投钱;01:投入0.5;10:投入1;
输出 00:不出可乐;10:出可乐,不找零;11:出可乐,找零;
由于状态较多(人懒)假定2.5就出可乐,设计mealy(输出与输入和状态都有关系)状态机如下图所示:
在输入上采用组合逻辑电路,所以不会有时钟延时现象,但是在状态改变和输出为时序逻辑电路(与输入和电路原来的状态有关,结构上为组合逻辑电路+存储电路),所以电平会延时一个时钟周期
module complex_fsm ( input wire sys_clk, input wire sys_res, input wire pi_money_half, input wire pi_money_one, output reg po_cola, output reg po_money ); /* 可以更改为简单的写法 parameter IDLE = 5'b00001; parameter HALF = 5'b00010; parameter ONE = 5'b00100; parameter ONE_HALF = 5'b01000; parameter TWO = 5'b10000; */ parameter IDLE = 5'b00001, HALF = 5'b00010, ONE = 5'b00100, ONE_HALF = 5'b01000, TWO = 5'b10000; wire [1:0] pi_money; reg [5:0] state; assign pi_money = {pi_money_one,pi_money_half}; //观察波形可知pi_money为两个投币信号电平的拼接 always@(posedge sys_clk or negedge sys_res) if(sys_res == 1'b0) state <= IDLE; else case(state) IDLE : if(pi_money == 2'b01) state <= HALF; else if(pi_money == 2'b10) state <= ONE; else state <= IDLE; HALF : if(pi_money == 2'b01) state <= ONE; else if(pi_money == 2'b10) state <= ONE_HALF; else state <= HALF; ONE : if(pi_money == 2'b01) state <= ONE_HALF; else if(pi_money == 2'b10) state <= TWO; else state <= ONE; ONE_HALF : if(pi_money == 2'b01) state <= TWO; else if(pi_money == 2'b10) state <= IDLE; else state <= ONE_HALF; TWO : if((pi_money == 2'b01)||(pi_money == 2'b10)) state <= IDLE; else state <= TWO; default:state <= IDLE; endcase always@(posedge sys_clk or negedge sys_res) if(sys_res == 1'b0) po_cola <= 1'b0; else if((state == ONE_HALF && pi_money == 2'b10) ||(state == TWO && pi_money == 2'b01) ||(state == TWO && pi_money == 2'b10)) po_cola <= 1'b1; else po_cola <= 1'b0; always@(posedge sys_clk or negedge sys_res) if(sys_res == 1'b0) po_money <= 1'b0; else if(state == TWO && pi_money == 2'b10) po_money <= 1'b1; else po_money <= 1'b0; endmodule
`timescale 1ns/1ns module tb_complex_fsm(); reg sys_clk; reg sys_res; reg pi_money_half; reg pi_money_one; reg random_data; // 随机数 wire po_cola; wire po_money; initial begin sys_clk = 1'b1; sys_res <= 1'b0; #20 sys_res <= 1'b1; end always #10 sys_clk = ~sys_clk; always@(posedge sys_clk or negedge sys_res) //随机电平的产生 if(sys_clk == 1'b0) random_data <= 1'b0; else random_data <= {$random} % 2; always@(posedge sys_clk or negedge sys_res) if(sys_clk == 1'b0) // 因为低电平时不检测,所以模拟同样保持 pi_money_half <= 1'b0; else pi_money_half <= random_data; always@(posedge sys_clk or negedge sys_res) if(sys_clk == 1'b0) pi_money_one <= 1'b0; else pi_money_one <= ~random_data; // 因为一次只能投一个币,所以one和half不可能同时为高电平 wire [1:0] pi_money = cfm1.pi_money; wire [4:0] state = cfm1.state; // 将模块内部的变量以此种形式引出,引出名为实例化后的名称,方便后边的监视函数进行具体监视 initial begin $timeformat(-9,0,"ns",6); // 打印时间格式 // 变量监视,记得后边赋值 $monitor("@time %t:pi_money_half = %b,pi_money_one = %b,pi_money = %b,state = %b,po_cola = %b,po_money = %b",$time,pi_money_half,pi_money_one,pi_money,state,po_cola,po_money); end complex_fsm cfm1 ( .sys_clk(sys_clk), .sys_res(sys_res), .pi_money_half(pi_money_half), .pi_money_one(pi_money_one), .po_cola(po_cola), .po_money(po_money) ); endmodule