LinCoding

【读书笔记】详细解析基于三段式状态机的流水灯

0
阅读(4857)

【主题】:详细解析基于三段式状态机的流水灯

【作者】:LinCoding

【时间】:2016.11.16

对于学习Verilog的同学来说,状态机并不陌生,状态机分为一段式、两段式和三段式,具体优缺点请大家百度。网上一大把,本文笔者将以一个基于三段式流水灯为例,详细解析下三段式状态机,一方面,加深自己的理解,另一方面,如有见解有误,请大家指出。

(源码出自CrazyBingo,尊重版权)

`timescale 1ns/1ns module led_water #( parameter LED_WIDTH = 8 ) ( input clk, //global clock input rst_n, //global reset output reg [LED_WIDTH-1'b1:0] led_data );

第一部分是输入输出定义,没什么好说的。

//----------------------------------- //200ms counter localparam DELAY_TOP = 24'd10_000_000; //localparam DELAY_TOP = 24'd16; //just for simulation reg [23:0] delay_cnt; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) delay_cnt <= 24'd0; else if ( delay_cnt < DELAY_TOP ) delay_cnt <= delay_cnt + 1'b1; else delay_cnt <= 24'd1; end wire delay_done = ( delay_cnt == DELAY_TOP ) ? 1'b1 : 1'b0;

第二部分是一个200ms的计数器,没什么好说的。

运用了上篇文章的一个模式:即,计数器的输出用组合逻辑

//----------------------------------- //FSM: part 1 reg [3:0] current_state; reg [3:0] next_state; always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) current_state <= 4'd0; else if ( delay_done ) current_state <= next_state; else current_state <= current_state; end

重点来了,三段式有限状态机第一段,很简单,先声明当前态下一态,然后,在计数计到200ms时候,将下一态的值赋予当前态,否则,保持在当前态不变。

也就是说,第一段状态机描述了状态变迁的条件

有一点需要注意,这里复位时,只将current_state清零,不清零next_state

//----------------------------------- //FSM: part 2 always @ ( * ) begin next_state = 4'd0; case ( current_state ) 4'd0: next_state = 4'd1; 4'd1: next_state = 4'd2; 4'd2: next_state = 4'd3; 4'd3: next_state = 4'd4; 4'd4: next_state = 4'd5; 4'd5: next_state = 4'd6; 4'd6: next_state = 4'd7; 4'd7: next_state = 4'd8; 4'd8: next_state = 4'd9; 4'd9: next_state = 4'd10; 4'd10: next_state = 4'd11; 4'd11: next_state = 4'd12; 4'd12: next_state = 4'd13; 4'd13: next_state = 4'd0; default:next_state = 4'd0; endcase end

三段式FSM第二段,有四点需要注意。

1、本段是组合逻辑块,因此always的敏感列表写的是( * ),并且,用的是阻塞赋值语句

2、case的条件是current_state,也就是说,本段是根据当前态来找到下一态,可以说是描述状态变迁的内容(变迁到哪一个态)

3、上电后,由于组合逻辑,而current_state的复位值为0,那么,next_state的值会是1。如下图所示。

blob.png

4、next_state= 4'd0; 这一句,写不写无所谓,看个人喜好

//----------------------------------- //FSM: part 3 always @ ( posedge clk or negedge rst_n ) begin if ( ! rst_n ) led_data <= 8'b0000_0001; else if ( delay_done ) case ( next_state ) 4'd0: led_data <= 8'b0000_0001; 4'd1: led_data <= 8'b0000_0010; 4'd2: led_data <= 8'b0000_0100; 4'd3: led_data <= 8'b0000_1000; 4'd4: led_data <= 8'b0001_0000; 4'd5: led_data <= 8'b0010_0000; 4'd6: led_data <= 8'b0100_0000; 4'd7: led_data <= 8'b1000_0000; 4'd8: led_data <= 8'b0100_0000; 4'd9: led_data <= 8'b0010_0000; 4'd10: led_data <= 8'b0001_0000; 4'd11: led_data <= 8'b0000_1000; 4'd12: led_data <= 8'b0000_0100; 4'd13: led_data <= 8'b0000_0010; default:led_data <= 8'b0000_0001; endcase else led_data <= led_data;

三段式FSM最后一段,注意三点:

1、case中为next_state,也就是根据next_state,来确定输出。可以说第三段描述的是状态变迁的输出

2、复位时,复位第一态,即:led_data <= 8'b0000_0001;

3、为什么要有delay_done的条件,直接写如下代码不可以吗?

else case ( next_state ) 4'd0: led_data <= 8'b0000_0001; 4'd1: led_data <= 8'b0000_0010; .......................................

不可以的,

(1)如果没有delay_done的限制,那么在复位结束后,led_data会直接根据next_state的值,也就是1,来进行输出,这时,输出的值就是8'b0000_0010,而8'b0000_0001停留的时间几乎没有。见下图。

blob.png

(2)由于led_data的输出只与next_state有关,导致了边沿不能对齐。见下图

blob.png

很明显,led_data的输出晚了一个CLK,原因就是由于刚复位结束后,led_data直接输出了next_state等于1的状态,导致后面都延迟了一个状态,唉,这有点说不清楚的感觉,大家还是最好自己仿真一下,看下结果就知道了。

加上delay_done以后,一切都变得很和谐。

blob.png

总结:虽然一个小小的三段式状态机,可是要注意的问题还是很多的

模式总结:

1、FSM第一段:描述状态变迁的条件,只对current_state进行复位清零

2、FSM第二段:描述状态变迁的内容,通通为组合逻辑,case中为current_state,根据当前态来确定下一态的内容。

3、FSM第三段:描述状态变迁的输出,首先复位时,要复位第一态,其次,要有一个使能条件(delay_done),最后case中放的是next_state,是根据next_state来确定输出。


掌握了这几点,状态机真是一点也不难掌握。


以上内容仅为笔者个人见解,如有不同观点,欢迎交流!







Baidu
map