有限状态机
1 状态机原理
Verilog HDL语句块都是并行执行的,若想按照顺序的方式执行语句,则会使用有限状态机,简称为状态机。
状态机的设计分为一段式、二段式和三段式。通常使用两段式状态机和三段式状态机。
1.1 二段式状态机
二段式状态机使用一个时序逻辑进行状态的转移,另一个时序逻辑进行数据的输出,模板如下所示:
//二段式状态机模板 module 模块名( 端口1, 端口2, ... 端口n ) //用独热码描述状态 localparam 状态1 = n'b0000...0001; localparam 状态2 = n'b0000...0010; ... localparam 状态n = n'b1000...0000; reg [位宽] state; //当前状态 //时序逻辑描述的状态转移 always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin state <= 默认状态; end else case(state) 状态1: begin if (状态转移条件) begin state <= 下一个状态; end else begin state <= state; end end 状态2: begin ... end ... 状态n: begin ... end default begin state = 默认状态; end endcase end //时序逻辑描述的数据输出 always @(posedge clk or negedge rst_n) begin 数据输出; end endmodule
1.2 三段式状态机
三段式状态机将二段式状态机中进行状态转移的时序逻辑分为两个部分,其中一部分用时序逻辑实现状态的存储,另一部分用组合逻辑实现状态的跳转。与二段式状态机相同的是,都用一个时序逻辑进行数据的输出,模板如下所示:
//三段式状态机模板 module 模块名( 端口1, 端口2, ... 端口n ) //用独热码描述状态 localparam 状态1 = n'b0000...0001; localparam 状态2 = n'b0000...0010; ... localparam 状态n = n'b1000...0000; reg [位宽] curr_state; //当前状态 reg [位宽] next_state; //接下来的状态 //时序逻辑描述实现状态curr_state的存储 always @(posedge clk or posedge rst_n) begin if (rst_n == 1'b0) begin curr_state <= 默认状态; end else begin curr_state <= next_state; end end //组合逻辑实现状态next_state的跳转 always @(posedge clk or negedge rst_n) begin case(curr_state) 状态1: begin if (状态转移条件) begin next_state <= 下一个状态; end else begin next_state <= 状态1; end end 状态2: begin ... end ... 状态n: begin ... end default begin next_state = 默认状态; end endcase end //时序逻辑描述的数据输出 always @(posedge clk or negedge rst_n) begin 数据输出; end endmodule
三段式状态机将组合逻辑和时序逻辑分开的好处就是将来在修时序的时候方便插入寄存器。
2 实例:自动售货机
使用状态机描述一个简单的自动售货机,该售货机中的商品3元一件,每次投币只能投入1元。在我们描述状态机之前,一般会先画出对应的状态转移图,该状态转移图如图 1 所示。
根据状态转移图,我们可以得到如下所示的Verilog HDL代码:
2.1 两段式
module fsm( input wire clk, input wire rst_n, input wire pi_money, output reg po_cola ); //用独热码描述状态 localparam IDLE = 3'b001; localparam ONE = 3'b010; localparam TWO = 3'b100; //两段式状态机 //状态寄存器(两段式) reg [2:0] state; //该always块描述状态转移:state always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin state <= IDLE; end else case(state) IDLE: begin if (pi_money == 1'b1) begin state <= ONE; end else begin state <= state; end end ONE: begin if (pi_money == 1'b1) begin state <= TWO; end else begin state <= state; end end TWO: begin if (pi_money == 1'b1) begin state <= IDLE; end else begin state <= state; end end default: begin state <= IDLE; end endcase end //该always快描述数据输出:po_cola always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin po_cola <= 1'b0; end else if (state == TWO && pi_money == 1'b1) begin po_cola <= 1'b1; end else begin po_cola <= 1'b0; end end
2.2 三段式
module fsm( input wire clk, input wire rst_n, input wire pi_money, output reg po_cola ); //用独热码描述状态 localparam IDLE = 3'b001; localparam ONE = 3'b010; localparam TWO = 3'b100; //三段式寄存器 (将两段式寄存器第一个always块拆分为2个部分,一个部分用时序逻辑,另一个部分用组合逻辑) //curr_state用时序逻辑实现 reg [2:0] curr_state; //next_state用组合逻辑实现 reg [2:0] next_state; //时序逻辑实现状态的存储 always @(posedge clk or posedge rst_n) begin if (rst_n == 1'b0) begin curr_state <= IDLE; end else begin curr_state <= next_state; end end //组合逻辑实现状态的跳转 always @(*) begin case(curr_state) IDLE: begin if (pi_money == 1'b1) begin next_state = ONE; end else begin next_state = IDLE; end end ONE: begin if (pi_money == 1'b1) begin next_state = TWO; end else begin next_state = ONE; end end TWO: begin if (pi_money == 1'b1) begin next_state = IDLE; end else begin next_state = TWO; end end default: begin next_state = IDLE; end endcase end //该always快描述数据输出:po_cola always @(posedge clk or negedge rst_n) begin if (rst_n == 1'b0) begin po_cola <= 1'b0; end else if (curr_state == TWO && pi_money == 1'b1) begin po_cola <= 1'b1; end else begin po_cola <= 1'b0; end end
本文作者:Yamada_Ryo
本文链接:https://www.cnblogs.com/little55/p/18195874
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步