验证实战

参考链接

基础1

需要注意的是

根据状态机的输出是否与输入有关,可以分为下列两种状态机:

  1. Moore型:输出只与当前状态有关,与输入无关;
  2. Mealy型:输出不仅与当前状态有关,还与输入有关。

这两种状态机在电路实现上的区别如下图所示[1],当然对这种状态机的具体区别和转换等可以参考[1]

imgMoore型状态机

imgMealy型状态机

此外,对各个状态进行编码,又有以下几种编码方式:

  1. 二进制:根据状态数量进行二进制累加编码;
  2. 格雷码:相邻状态的编码只差1bit;
  3. 独热码:每个状态的编码只有1bit为1,其他为0;

分别采用这三种编码对上述四个状态进行编码,如下图所示

img

可以明显看到,二进制和格雷码比独热码更节省寄存器,但独热码具有节省组合逻辑的优点[2],独热码也是在状态机的设计当中较为常用的状态编码方式。

那我该咋写状态机?

比如问“输入串行序列,用状态机实现‘101’序列检测,若序列符合则输出1,否则输出0“,使用Verilog实现的具体流程是:

  1. 确定各状态:按照问题可以确定S0、S1、S2、S3四种状态,分别表示序列为xxx、xx1、x10、101四种情况,并采用独热码对状态编码,例如S0:4’b0001、S1:4’b0010、S2:4’b0100、S3:4’b1000;
  2. 确定状态转换条件:根据1可知,当序列输入为1时,S0->S1,当序列输入为0时,S1->S2,以此类推;
  3. 确定状态输出:根据1可知,当S0时输出0,S1时输出0,S2时输出0,S3时输出1。

以下分别采用三种常见的状态机实现上述要求:

  • 一段式:直接粗暴写着爽

一段式即将“状态转移、状态输出和状态转移条件”写在一块,简单粗暴,写的飞起~

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
//一段式状态机
reg [3:0] state;
localparam S0 = 4'b0001, //采用独热码
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;

//共一段 采用时序逻辑完成状态机的转移、输出等
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
dout <= 1'b0;
state <= S0;
end
else begin
case(state)
S0:begin
dout <= 1'b0; //输出0
if (din == 1'b1) begin
state <= S1;
end
else begin
state <= S0;
end
end

S1:begin
dout <= 1'b0; //输出0
if (din == 1'b1) begin
state <= S1;
end
else begin
state <= S2;
end
end

S2:begin
dout <= 1'b0; //输出0
if (din == 1'b1) begin
state <= S3;
end
else begin
state <= S0;
end
end

S3:begin
dout <= 1'b1; //输出1
if (din == 1'b1) begin
state <= S1;
end
else begin
state <= S2;
end
end

default:begin
dout <= 1'b0;
state <= S0;
end
endcase
end
end

一段式的状态机的RTL如下图所示:

img一段式状态机RTL

一段式的状态机的RTL综合后的结果如下图所示:

img一段式状态机RTL综合后的结果

  • 两段式:容易理解不难写⭐⭐⭐

两段式即将“状态转移”、“状态输出和状态转移条件”分成两部分写,容易理解,便于维护。(经评论区提醒组合逻辑不用复位)

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
//两段式状态机
reg [3:0] current_state; //当前状态
reg [3:0] next_state; //下一状态
localparam S0 = 4'b0001, //采用独热码
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;

//第一段 采用时序逻辑完成状态转移
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
current_state <= S0;
end
else begin
current_state <= next_state;
end
end

//第二段 采用组合逻辑完成转移条件判断和状态输出
always @(*) begin
case(current_state)
S0:begin
dout = 1'b0; //输出0
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S0;
end
end

S1:begin
dout = 1'b0; //输出0
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S2;
end
end

S2:begin
dout = 1'b0; //输出0
if (din == 1'b1) begin
next_state = S3;
end
else begin
next_state = S0;
end
end

S3:begin
dout = 1'b1; //输出1
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S2;
end
end

default:begin
dout = 1'b0;
next_state = S0;
end
endcase
end

两段式的状态机的RTL如下图所示:

img两段式状态机RTL

两段式的状态机的RTL综合后的情况如下图所示:

img两段式状态机RTL综合后的结果

  • 三段式:结构精简八股文⭐⭐⭐⭐⭐

三段式即将“状态转移”、“转移条件”、“状态输出”分三部分写,可能在看起来会比较的难写,但是了解结构之后感觉就像八股文或是填空题一样。但需要注意的是:第三段不一定是采用一个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
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
//三段式状态机
reg [3:0] current_state; //当前状态
reg [3:0] next_state; //下一状态
localparam S0 = 4'b0001, //采用独热码
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;

//第一段 采用时序逻辑完成状态转移
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
current_state <= S0;
end
else begin
current_state <= next_state;
end
end

//第二段 采用组合逻辑完成转移条件判断
always @(*) begin
case(current_state)
S0:begin
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S0;
end
end

S1:begin
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S2;
end
end

S2:begin
if (din == 1'b1) begin
next_state = S3;
end
else begin
next_state = S0;
end
end

S3:begin
if (din == 1'b1) begin
next_state = S1;
end
else begin
next_state = S2;
end
end

default:next_state = S0;
endcase
end

//第三段 采用时序逻辑状态输出
always @(posedge clk or posedge rst) begin
if (rst) begin
// reset
dout <= 1'b0;
end
else begin
case(next_state) //采用next state,提前一拍
S0:begin
dout <= 1'b0; //输出0
end

S1:begin
dout <= 1'b0; //输出0
end

S2:begin
dout <= 1'b0; //输出0
end

S3:begin
dout <= 1'b1; //输出1
end

default:dout <= 1'b0;
endcase
end
end

三段式的状态机的RTL如下图所示:

img三段式状态机RTL

三段式的状态机的RTL综合后的结果如下图所示:

img三段式状态机RTL综合后的结果

总的来说,三段式状态机是较为常用的写法。

其他问题

问题1 FPGA编写有限状态机使用独热码为什么会占用较少的组合逻辑电路?

格雷码:相邻之间只变1bit,编码密度高。

独热码:任何状态只有1bit为1,其余皆为0,编码密度低。

比如说,表示4个状态,那么状态机寄存器采用格雷码编码只需要2bit:00(S0),01(S1),11(S2),10(S3);

采用独热码需要4bit:0001(S0),0010(S1),0100(S2),1000(S3)。所以很明显采用格雷码可以省2bit寄存器。

难理解的是,为什么独热码更节省组合逻辑:

其实很简单,

例一:

假如我们要在代码中判断状态机是否处于某状态S1,

对于格雷码的状态机来说,代码是这样的:assign S1 = (STATUS==2’b01);

对于独热码来说,代码是这样的就行:assign S1=STATUS[1];

所以独热码的译码非常简单。

例二:

考虑最简单的跳变,当A为1时,状态机会从S0跳到S1:。

采用格雷码写:

STATUS[1:0] <= (STATUS==2’h00) & A ? 2’h01 : 2’h00;

采用独热码写:

STATUS[1] <= STATUS[0] & A;

有人怀疑这里的逻辑,认为只check独热码的一个bit有问题。当然是没问题的,0110,0011等编码属于不care的编码,在卡诺图化简中,不care的编码可以与其余的有效编码合并化简。实际上综合器也会这么做,所以独热码非常容易化简。

假如说S0跳到S1条件为A;S1跳到S2条件为B;S2跳到S3条件为C;S3跳到S0条件为D;

那么整个状态机化简之后代码就是:

STATUS[0] <= STATUS[3] & D;

STATUS[1] <= STATUS[0] & A;

STATUS[2] <= STATUS[1] & B;

STATUS[3] <= STATUS[2] & C;

总结一下:

独热码适合写条件复杂但是状态少的状态机;

格雷码适合写条件不复杂但是状态多的状态机。

preview

https://www.zhihu.com/question/40994717

问一下Data Array实现的SRAM,以及读写时序的问题(延一个周期)

TLB与地址映射的问题

MMIO和Cache的问题


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!