verilog状态机

​ 根据状态机的输出是否与输入条件相关,可将状态机分为两大类,即摩尔(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 define
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 define
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
/*
* AXI4-Lite RAM
*/
module axil_ram #
(
// Width of data bus in bits
parameter DATA_WIDTH = 32,
// Width of address bus in bits
parameter ADDR_WIDTH = 16,
// Width of wstrb (width of data bus in words)
parameter STRB_WIDTH = (DATA_WIDTH/8),
// Extra pipeline register on output
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;

// (* RAM_STYLE="BLOCK" *)
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;


/*==============axi ram write=================*/
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

/*==============axi ram read=================*/

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
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/09/24 14:57:55
// Design Name:
// Module Name: tb_mdio_top
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


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_MDIO_I = ETH0_MDIO;
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

verilog状态机
http://witbit.cn/FPGA/verilog状态机.html
作者
朝彻
发布于
2025年2月13日
许可协议