根据状态机的输出是否与输入条件相关,可将状态机分为两大类,即摩尔(Moore)型状态机和米勒(Mealy) 型状态机。
➢ Mealy 状态机:组合逻辑的输出不仅取决于当前状态,还取决于输入状态。 ➢ Moore 状态机:组合逻辑的输出只取决于当前状态。
状态机分类 根据状态机的实际写法,状态机还可以分为一段式、二段式和三段式状态机。
一段式: 整个状态机写到一个 always 模块里面,在该模块中既描述状态转移,又描述状态的输入和输 出。不推荐采用这种状态机,因为从代码风格方面来讲,一般都会要求把组合逻辑和时序逻辑分开;从代 码维护和升级来说,组合逻辑和时序逻辑混合在一起不利于代码维护和修改,也不利于约束。
二段式: 用两个 always 模块来描述状态机,其中一个 always 模块采用同步时序描述状态转移;另一个 模块采用组合逻辑判断状态转移条件,描述状态转移规律以及输出。不同于一段式状态机的是,它需要定 义两个状态,现态和次态,然后通过现态和次态的转换来实现时序逻辑。
三段式: 在两个 always 模块描述方法基础上,使用三个 always 模块,一个 always 模块采用同步时序描 述状态转移,一个 always 采用组合逻辑判断状态转移条件,描述状态转移规律,另一个 always 模块描述状 态输出(可以用组合电路输出,也可以时序电路输出)。
实际应用中三段式状态机使用最多,因为三段式状态机将组合逻辑和时序分开,有利于综合器分析优 化以及程序的维护;并且三段式状态机将状态转移与状态输出分开,使代码看上去更加清晰易懂,提高了 代码的可读性,推荐大家使用三段式状态机,本文也着重讲解三段式。 三段式状态机的基本格式是:
第一个 always 语句实现同步状态跳转; 第二个 always 语句采用组合逻辑判断状态转移条件; 第三个 always 语句描述状态输出(可以用组合电路输出,也可以时序电路输出)。在开始编写状态机代码之前,一般先画出状态跳转图,这样在编写代码时思路会比较清晰,下面以一个 7 分频为例(对于分频等较简单的功能,可以不使用状态机,这里只是演示状态机编写的方法),状态跳转图如下图所示:
状态跳转图画完之后,接下来通过 parameter 来定义各个不同状态的参数,如下代码所示:
1 2 3 4 5 6 7 parameter S0 = 7'b0000001 ; parameter S1 = 7'b0000010 ;parameter S2 = 7'b0000100 ;parameter S3 = 7'b0001000 ;parameter S4 = 7'b0010000 ;parameter S5 = 7'b0100000 ;parameter S6 = 7'b1000000 ;
这里是使用独热码的方式来定义状态机,每个状态只有一位为 1,当然也可以直接定义成十进制的 0, 1,2……7。 因为我们定义成独热码的方式,每一个状态的位宽为 7 位,接下来还需要定义两个 7 位的寄存器,一 个用来表示当前状态,另一个用来表示下一个状态,如下所示:
1 2 reg [6 :0 ] curr_st ; reg [6 :0 ] next_st ;
接下来就可以使用三个 always 语句来开始编写状态机的代码,第一个 always 采用同步时序描述状态转 移,第二个 always 采用组合逻辑判断状态转移条件,第三个 always 是描述状态输出,一个完整的三段式状 态机的例子如下代码所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 module divider7_fsm ( input sys_clk , input sys_rst_n , output reg clk_divide_7 );parameter S0 = 7'b0000001 ; parameter S1 = 7'b0000010 ;parameter S2 = 7'b0000100 ;parameter S3 = 7'b0001000 ;parameter S4 = 7'b0010000 ;parameter S5 = 7'b0100000 ;parameter S6 = 7'b1000000 ; reg [6 :0 ] curr_st ; reg [6 :0 ] next_st ; always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) curr_st <= S0;else curr_st <= next_st;end always @(*) begin case (curr_st) S0: next_st = S1; S1: next_st = S2; S2: next_st = S3; S3: next_st = S4; S4: next_st = S5; S5: next_st = S6; S6: next_st = S0; default : next_st = S0;endcase end always @(posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) clk_divide_7 <= 1'b0 ;else if ((curr_st == S0) | (curr_st == S1) | (curr_st == S2) | (curr_st == S3)) clk_divide_7 <= 1'b0 ;else if ((curr_st == S4) | (curr_st == S5) | (curr_st == S6)) clk_divide_7 <= 1'b1 ; else ;end endmodule
AXI总线为例
AXI Write time sequence
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ___ ___ ___ ___ ___ clk ___/ \___/ \___/ \___/ \___/ \___ _______ awid XXXX_ID____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awaddr XXXX_ADDR__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awlen XXXX_00____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awsize XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awburst XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awprot XXXX_PROT__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ awvalid ___/ \_______________________________ ___________ _______________________ awready \_______/ _______________ wdata XXXX_DATA__________XXXXXXXXXXXXXXXXXXXXXXXX _______________ wstrb XXXX_STRB__________XXXXXXXXXXXXXXXXXXXXXXXX _______________ wvalid ___/ \_______________________ _______ wready ___________/ \_______________________ _______ bid XXXXXXXXXXXXXXXXXXXXXXXXXXXX_ID____XXXXXXXX _______ bresp XXXXXXXXXXXXXXXXXXXXXXXXXXXX_RESP__XXXXXXXX _______ bvalid ___________________________/ \_______ ___________________________________________ bready
AXI Read time sequence
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ___ ___ ___ ___ ___ clk ___/ \___/ \___/ \___/ \___/ \___ _______ arid XXXX_ID____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ araddr XXXX_ADDR__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ arlen XXXX_00____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ arsize XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ arburst XXXX_0_____XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ arprot XXXX_PROT__XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX _______ arvalid ___/ \_______________________________ ___________________________________________ arready _______ rid XXXXXXXXXXXXXXXXXXXXXXXXXXXX_ID____XXXXXXXX _______ rdata XXXXXXXXXXXXXXXXXXXXXXXXXXXX_DATA__XXXXXXXX _______ rresp XXXXXXXXXXXXXXXXXXXXXXXXXXXX_RESP__XXXXXXXX _______ rvalid ___________________________/ \_______ ___________________________________________ rready
两段式axi-ram示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 module axil_ram # ( parameter DATA_WIDTH = 32 , parameter ADDR_WIDTH = 16 , parameter STRB_WIDTH = (DATA_WIDTH/8 ), parameter PIPELINE_OUTPUT = 0 ) ( input wire clk, input wire rst, input wire [ADDR_WIDTH-1 :0 ] s_axil_awaddr, input wire [2 :0 ] s_axil_awprot, input wire s_axil_awvalid, output wire s_axil_awready, input wire [DATA_WIDTH-1 :0 ] s_axil_wdata, input wire [STRB_WIDTH-1 :0 ] s_axil_wstrb, input wire s_axil_wvalid, output wire s_axil_wready, output wire [1 :0 ] s_axil_bresp, output wire s_axil_bvalid, input wire s_axil_bready, input wire [ADDR_WIDTH-1 :0 ] s_axil_araddr, input wire [2 :0 ] s_axil_arprot, input wire s_axil_arvalid, output wire s_axil_arready, output wire [DATA_WIDTH-1 :0 ] s_axil_rdata, output wire [1 :0 ] s_axil_rresp, output wire s_axil_rvalid, input wire s_axil_rready );parameter VALID_ADDR_WIDTH = ADDR_WIDTH - $clog2 (STRB_WIDTH);parameter WORD_WIDTH = STRB_WIDTH;parameter WORD_SIZE = DATA_WIDTH/WORD_WIDTH;reg mem_wr_en;reg mem_rd_en;reg s_axil_awready_reg = 1'b0 , s_axil_awready_next;reg s_axil_wready_reg = 1'b0 , s_axil_wready_next;reg s_axil_bvalid_reg = 1'b0 , s_axil_bvalid_next;reg s_axil_arready_reg = 1'b0 , s_axil_arready_next;reg [DATA_WIDTH-1 :0 ] s_axil_rdata_reg = {DATA_WIDTH{1'b0 }}, s_axil_rdata_next;reg s_axil_rvalid_reg = 1'b0 , s_axil_rvalid_next;reg [DATA_WIDTH-1 :0 ] s_axil_rdata_pipe_reg = {DATA_WIDTH{1'b0 }};reg s_axil_rvalid_pipe_reg = 1'b0 ;reg [DATA_WIDTH-1 :0 ] mem[(2 **VALID_ADDR_WIDTH)-1 :0 ];wire [VALID_ADDR_WIDTH-1 :0 ] s_axil_awaddr_valid = s_axil_awaddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH);wire [VALID_ADDR_WIDTH-1 :0 ] s_axil_araddr_valid = s_axil_araddr >> (ADDR_WIDTH - VALID_ADDR_WIDTH);assign s_axil_awready = s_axil_awready_reg;assign s_axil_wready = s_axil_wready_reg;assign s_axil_bresp = 2'b00 ;assign s_axil_bvalid = s_axil_bvalid_reg;assign s_axil_arready = s_axil_arready_reg;assign s_axil_rdata = PIPELINE_OUTPUT ? s_axil_rdata_pipe_reg : s_axil_rdata_reg;assign s_axil_rresp = 2'b00 ;assign s_axil_rvalid = PIPELINE_OUTPUT ? s_axil_rvalid_pipe_reg : s_axil_rvalid_reg;always @* begin mem_wr_en = 1'b0 ; s_axil_awready_next = 1'b0 ; s_axil_wready_next = 1'b0 ; s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; if (s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready)) begin s_axil_awready_next = 1'b1 ; s_axil_wready_next = 1'b1 ; s_axil_bvalid_next = 1'b1 ; mem_wr_en = 1'b1 ; end end always @(posedge clk) begin s_axil_awready_reg <= s_axil_awready_next; s_axil_wready_reg <= s_axil_wready_next; s_axil_bvalid_reg <= s_axil_bvalid_next; for (i = 0 ; i < WORD_WIDTH; i = i + 1 ) begin if (mem_wr_en && s_axil_wstrb[i]) begin mem[s_axil_awaddr_valid][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; end end if (rst) begin s_axil_awready_reg <= 1'b0 ; s_axil_wready_reg <= 1'b0 ; s_axil_bvalid_reg <= 1'b0 ; end end always @* begin mem_rd_en = 1'b0 ; s_axil_arready_next = 1'b0 ; s_axil_rvalid_next = s_axil_rvalid_reg && !(s_axil_rready || (PIPELINE_OUTPUT && !s_axil_rvalid_pipe_reg)); if (s_axil_arvalid && (!s_axil_rvalid || s_axil_rready || (PIPELINE_OUTPUT && !s_axil_rvalid_pipe_reg)) && (!s_axil_arready)) begin s_axil_arready_next = 1'b1 ; s_axil_rvalid_next = 1'b1 ; mem_rd_en = 1'b1 ; end end always @(posedge clk) begin s_axil_arready_reg <= s_axil_arready_next; s_axil_rvalid_reg <= s_axil_rvalid_next; if (mem_rd_en) begin s_axil_rdata_reg <= mem[s_axil_araddr_valid]; end if (!s_axil_rvalid_pipe_reg || s_axil_rready) begin s_axil_rdata_pipe_reg <= s_axil_rdata_reg; s_axil_rvalid_pipe_reg <= s_axil_rvalid_reg; end if (rst) begin s_axil_arready_reg <= 1'b0 ; s_axil_rvalid_reg <= 1'b0 ; s_axil_rvalid_pipe_reg <= 1'b0 ; end end endmodule
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 `timescale 1ns / 1ps module tb_mdio_top(); reg sys_clk;reg sys_rst_n;reg ETH0_MDC;reg ETH0_MDIO;wire ETH0_MDIO_I;wire ETH0_MDC_I;reg [32 :0 ] mdio_request1 = 64'hFFFFFFFF ;reg [32 :0 ] mdio_request2 = 64'b00000000000110 ;reg [7 :0 ]cnt;assign ETH0_MDIO_I = (cnt < 'd46 ) ? ETH0_MDIO : 1'bz ;assign ETH0_MDIO_O = (cnt < 'd46 ) ? 1'bz : ETH0_MDIO_I;assign ETH0_MDC_I = ETH0_MDC;initial begin sys_clk = 1'b0 ; sys_rst_n = 1'b0 ; #200 sys_rst_n = 1'b1 ;end always #100 sys_clk = ~sys_clk;always @ (posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) begin ETH0_MDC <= 1'b0 ; end else begin ETH0_MDC <= ~ETH0_MDC; end end always @ (posedge ETH0_MDC or negedge sys_rst_n) begin if (!sys_rst_n) begin cnt <= 0 ; ETH0_MDC <= 1'b0 ; end else begin cnt <= cnt + 1'b1 ; if (cnt == 'd64 ) cnt <= 0 ; else if (cnt < 'd32 ) begin ETH0_MDIO <= mdio_request1[cnt]; end else begin ETH0_MDIO <= mdio_request2[cnt - 'd32 ]; end end end mdio_top u_mdio_top( .sys_clk (sys_clk), .sys_rst_n (sys_rst_n), .ETH0_MDC (ETH0_MDC_I), .ETH0_MDIO (ETH0_MDIO_I) );endmodule