跟着我从零开始入门FPGA(一周入门XXOO系列)-6、有限状态机
发布者:jackzhang
时间:2015-03-17 09:01:57
(本连载共七部分,这是第六部分)
作者:McuPlayer2013 (EETOP FPGA版块版主) 原帖地址:http://bbs.eetop.cn/thread-385362-1-1.html)
以下是正文:
6、有限状态机
状态机,只要C代码写过2年的人,估计无人不识君,稍微复杂的逻辑都可以借助状态机来简化问题。
为了方便,我们使用前面用过的一个例子,来说明状态机的应用,也就是说我们前面已经有意无意的用过状态机了。
我们以SPI的Slave接口,为例,来说明状态机的使用
为了简化问题
1、我们没有把信号同步到本地时钟
2、把其他信号同步到SCK
3、我们把SPI暂时按照单向来分析
下面,我们分析SPI通讯
1、nCS高电平时候,总线是空闲的
2、nCS低电平时传输数据
3、满了8个bit,凑够了一个字节,要保存当前已经收到的字节,并准备收下一个;
nCS高电平的时候,我们称之为idel态(IDEL)
接受0~7逐个bit的时候,称之为bit接受态(BIT_RECV)
收满一个字节,称之为字节转存态(BYTE_SAVE)
我们开始画状态转移图
model SlaveSPI(input nCS, input SCK, input MOSI, output MISO);
parameter IDEL = 0,
BIT_RECV = 1,
BIT_SAVE = 2;
reg[3:0] bitcnt;
reg[7:0] shift_in; //写入
reg[7:0] shift_out; //读出
reg[7:0] data;
reg[1:0] state;
reg[1:0] next_state;
always @(*)
begin
case(state)
IDEL:
if(nCS==1'b1)
next_state = IDEL;
else
next_state = BIT_RECV;
BIT_RECV:
if(nCS==1'b0)
bgein
if(bitcnt<4'h8)
next_state = BIT_RECV;
else
next_state = BYTE_SAVE;
end
else
next_state = IDEL;
BYTE_SAVE:
if(nCS==1'b0)
next_state = BIT_RECV;
else
next_state = IDEL;
defalut:
next_state = IDEL;
endcase
end
always @(posdge SCK)
if(nCS)
bitcnt=0;
else
state = next_state
always @(posdge SCK)
case(state)
BIT_RECV:
begin
bitcnt <= bitcnt+4'h1;
shift_in <= {shift_in[6:0], MOSI};
end
BYTE_SAVE:
begin
bitcnt <= 4'h0;
data <= deshift_in;
end
endcase
我用了所谓的三段式,来描述这个状态机,用了3个always语句
第一个always用来描述状态转移的条件
第二个always用来描述状态转移
第三个always用来描述状态机的输出,也就是状态机实际要干的活
与前面帖子(同步和异步设计)中的SPI代码相比,是不是冗长了很多。
冗长,并不代表着脱裤子放屁,自找麻烦。您难道没有反显,代码很容易读懂了吗?
没错,这就是空间换时间的策略,我们写更长的代码来增强可分析性。
状态机的代码撰写一般不复杂,复杂的是状态机的构建过程,这个过程中,我们要分析
实际遇到的各个情况,同时把状态机进行优化,某些状态进行合并,某些状态去掉。
有人问,为何某些状态要去掉?
这要说下FSM的来头了,有限状态机,是有限的状态机。
自然界实际的状态机,往往起状态的数量,是非常大的,直接建模使用简直是劳民伤财。
而且,现实的往往是无限的状态机,这根本无法用于工程实现,所以有限状态机就横空出世了。
正如,软件算法中的DAG(Directed Acyclic Graph)有向无环图,比纯粹的图有实用价值。
二叉树,比多叉树,也更容易实现。
越说,软件和FPGA越近了。可谓是天下大势,分久必合,合久必分。