verilog 学习笔记(4) FSM三段式写法以及之水库问题

这两天终于学到有限状态机的部分了,可以说这道题算是我进入FSM之后很难的一道题。这个题自己第一遍基本是写不出来代码,然后结合CSDN上面一些博主写的代码分析了一下,找到思路重新写了一遍。最后得到的代码也不简洁,基本达到100行,而且用的算法比答案给得更加复杂(请允许我在Verilog中用“算法”这个跟硬件语言关系不大的词语)。

先回顾一下自己现在知道的做FSM的Methodology:

1,将具体问题抽象为逻辑问题:确定输入、输出变量,状态变量(现态与次态);确定电路状态个数,并明确电路各个状态的含义;

2,状态分配(编码):为电路各个状态编码(也就是给出对应的二进制数组,而这个二进制数组往往对应到各个触发器的状态);确定触发器位数,从而确定状态变量的位宽;

3,画电路转换图,要包含输入、输出、次态;

4,根据电路转换图写代码。一般来说,作为初学者,我觉得写三段式代码逻辑比较清晰,如果按照HDLBits的说法,就是:

(1)Transition logic,也就是状态转移。考虑在不同的现态和输入的情况下,次态的情况。这里我们一般先考虑现态,用case语句涵盖现态的各个情况,再在对应模块中用if-else条件句讨论输入的不同取值对应的次态。

  (2)  Sequential logic, 也就是时序逻辑部分。这里的时序逻辑部分从微观角度来说,其实就是受时钟脉冲控制的触发器的状态变化(因为电路的其他部分都不受时钟脉冲控制)。也就是说考虑在clk的控制下,电路从现态到次态的转变情况;注意由于复位信号的存在,这个部分往往需要讨论同步、异步复位信号。一般都是用if-else语句解决。

(3)Output logic, 也就是输出的部分。这部分一般也只是组合逻辑部分,按照现态和输入的不同情况写对应的输出。今天做的水库问题还只是Moore型FSM,不用考虑输入的影响。

回到这个题目,我认为这个题很难的一点就是审题。审题的困难体现在逻辑抽象,这道题我的第一感受就是很难找到现态、次态。甚至从题目描述来说,我以为我们需要引入三个状态变量,过去的状态用来比较水位高低,现在的状态用来确定FR1等流速阀门,还有个次态留给状态转移。这让我觉得无从下手。

后来反复阅读分析了其他博主的代码,我才大致明白,其实只需要两个状态变量。在这个题里面,状态是用来存储上一次水位检测时的情况的,而我们确定DFR时就是将当前水位(新测出的水位情况,也即输入的S,或次态)跟之前的水位测量情况(现态)作比较。换而言之,输入的水位测量情况既是输入变量,又是电路的次态。而按照这种分析思路,这个题代码的特殊点就在于后面写Output logic时,条件语句讨论的是“次态”的值,这跟我们传统认识中FSM的输出应该讨论的是现态的值不符,其实只是这个题目理解的特殊性而已。实际上按照题意,这里面的“次态”其实就是当前水位,也即“现态”;只不过我们需要引入过去的水位情况作为更早的状态。如果改个说法,我们也可以说过去水位是“早态”,现在水位是“现态”,那么我们的状态转移就发生在“早态”和“现态”之间了,只是不符合我们时序逻辑的用语习惯而已。

一旦明确了这个FSM的现态和次态,我们就可以开始确定电路状态个数并编码了。我们当然可以采用one-hot,但是按照习惯我还是使用了常规编码方式。从题目上面可以看出,水位高度一共有四种,低于S1,在S1和S2之间,在S2和S3之间,高于S3。我们可以简单地按照水位高度,将这个四个状态从小到大对应到0、1、2、3进行编码(这样编码在后面比较水位高度时也有好处,可以将水位高度的比较直接转换为码值的比较)。由于电路只有四个状态,我们的状态变量也只需要两位的位宽就可以了。

 reg [1:0] state, next;
//The variable next is the new state, which is equivalent to the outcome of s.
 parameter L = 2'b00, S12 = 2'b01, S23 = 2'b10, H = 2'b11;
//Consider 4 states, they are below s1,s1 to s2, s2 to s3, above s3.

值得一提的是在编码过程中,parameter用得比较多。它其实跟C语言里面的define很相似,相当于给一个常数赋个名字。用parameter可以极大方便我们调用电路的状态,同时让代码更易理解。这里我直接用L、H分别代表最低水位和最高水位,用S12和S23代表水位位于S1和S2、S2和S3之间的状态,非常易懂。

下一步就是写状态转移部分。存疑的是,我看到不同博主写的此题的四个状态的代码内容,似乎都默认了状态转换只发生在相邻水位之间,而不考虑跨位转换(辟如从最低位转换成最高位),因此再写状态转换的时候最低位和最高位的次态都分别只有两种情况。我现目前也不知道这是不是题目的什么默认条件。

 always @(*)
//Transistion logic, but the question default the transition of levels only happens between two adjacent states and itself.
        begin
            case (state)
                L: begin
                    if (s == 3'b000)
                        next = L;
                    else if (s == 3'b001)
                        next = S12;
                end
                S12: begin
                    if (s == 3'b000)
                        next = L;
                    else if (s == 3'b001)
                        next = S12;
                    else if (s == 3'b011)
                        next = S23;
                end
                S23: begin
                    if (s == 3'b001)
                        next = S12;
                    else if (s == 3'b011)
                        next = S23;
                    else if (s == 3'b111)
                        next = H;
                end
                H: begin
                    if (s == 3'b011)
                        next = S23;
                    else if (s == 3'b111)
                        next = H;
                end
                default next = L;//When the s check nothing, it is equal to the lowest level.
            endcase
        end

这部分代码在分析出来以后基本是照着转换图写就行了。

下面就是时序逻辑部分,这个部分一般都比较简单。我们还是分别讨论有无重置信号的情况。

always @(posedge clk)//Sequential part(Flip flops part).
        begin
        if (reset)
            state <= L;
    	else
            state <= next;
    end

这里还要注意一点,在三段式FSM中,组合逻辑的always块中都用阻塞赋值,而时序逻辑的always块中都用非阻塞赋值。

然后下面就是输出部分。按照题意和我们先前分析的,一旦当前水位(次态)确定,fr1,fr2,fr3就确定了,无非是在确定次态下再讨论两个状态水位高低而已。因此我们这里还是使用case语句先讨论次态的不同情况,再在对应情况下比较次态和现态,也即当前水位和上一次测量时的水位的高低。由于编码的关系,这里我们直接通过对应码值大小来代替水位高低的比较就可以了。

 always @(posedge clk)//Output logic.
        begin
            if (reset||next == L)
            {fr1,fr2,fr3,dfr} = 4'b1111;
            else begin
                case (next)
                    S12: begin
                        if (next<state)
                            {fr1,fr2,fr3,dfr} = 4'b1101;
                        else if (next>state)
                            {fr1,fr2,fr3,dfr} = 4'b1100;
                        else
                            begin
                        	{fr1,fr2,fr3} = 3'b110;
                        	dfr = dfr;
//When the level of water dosen't change, the value of dfr also keeps, which is important.
                            end
                    end
                    S23: begin
                        if (next<state)
                            {fr1,fr2,fr3,dfr} = 4'b1001;
                        else if (next>state)
                            {fr1,fr2,fr3,dfr} = 4'b1000;
                        else
                            begin
                       	 	{fr1,fr2,fr3} = 3'b100;
                        	dfr = dfr;
                            end
                    end
                    H: begin
                        if (next>state)
                        {fr1,fr2,fr3,dfr} = 4'b0000;
                        else if (next == state)
                            begin
                        	{fr1,fr2,fr3} = 3'b000;
                        	dfr = dfr;
                            end
                        else
                            begin
                            {fr1,fr2,fr3} = 3'b000;
                        	dfr = 1'b1;
                            end
                    end
                endcase
            end
        end

这部分我个人的感受就是写起来非常地繁琐(虽然其实逻辑比较清晰)。光这部分代码就占了五十多行。另外值得注意的是当水位没有变化时,dfr也不变,这里可以选择不写dfr=dfr,但是千万不要把水位没有变化时dfr的情况归到0、1这种定态中去。

至此,这道题的三个部分就结束了。我在文末会附上完整代码。另外根据参考答案的比对,很多代码是将这道题的电路状态划分为6个,甚至8个,以上情况代码的量会少一些,大概五十行左右就可以解决。等自己有空的时候再分析一下。

module top_module (
    input clk,
    input reset,
    input [3:1] s,
    output fr3,
    output fr2,
    output fr1,
    output dfr
); 
    reg [1:0] state, next;//The variable next is the new state, which is equivalent to the outcome of s.
    parameter L = 2'b00, S12 = 2'b01, S23 = 2'b10, H = 2'b11;//Consider 4 states, they are below s1,s1 to s2, s2 to s3, above s3.
    
    always @(*)//Transistion logic, but the question default the transition of levels only happens between two adjacent states and itself.
        begin
            case (state)
                L: begin
                    if (s == 3'b000)
                        next = L;
                    else if (s == 3'b001)
                        next = S12;
                end
                S12: begin
                    if (s == 3'b000)
                        next = L;
                    else if (s == 3'b001)
                        next = S12;
                    else if (s == 3'b011)
                        next = S23;
                end
                S23: begin
                    if (s == 3'b001)
                        next = S12;
                    else if (s == 3'b011)
                        next = S23;
                    else if (s == 3'b111)
                        next = H;
                end
                H: begin
                    if (s == 3'b011)
                        next = S23;
                    else if (s == 3'b111)
                        next = H;
                end
                default next = L;//When the s check nothing, it is equal to the lowest level.
            endcase
        end
    always @(posedge clk)//Sequential part(Flip flops part).
        begin
        if (reset)
            state <= L;
    	else
            state <= next;
    end
    
    always @(posedge clk)//Output logic.
        begin
            if (reset||next == L)
            {fr1,fr2,fr3,dfr} = 4'b1111;
            else begin
                case (next)
                    S12: begin
                        if (next<state)
                            {fr1,fr2,fr3,dfr} = 4'b1101;
                        else if (next>state)
                            {fr1,fr2,fr3,dfr} = 4'b1100;
                        else
                            begin
                        	{fr1,fr2,fr3} = 3'b110;
                        	dfr = dfr;//When the level of water dosen't change, the value of dfr also keeps, which is important.
                            end
                    end
                    S23: begin
                        if (next<state)
                            {fr1,fr2,fr3,dfr} = 4'b1001;
                        else if (next>state)
                            {fr1,fr2,fr3,dfr} = 4'b1000;
                        else
                            begin
                       	 	{fr1,fr2,fr3} = 3'b100;
                        	dfr = dfr;
                            end
                    end
                    H: begin
                        if (next>state)
                        {fr1,fr2,fr3,dfr} = 4'b0000;
                        else if (next == state)
                            begin
                        	{fr1,fr2,fr3} = 3'b000;
                        	dfr = dfr;
                            end
                        else
                            begin
                            {fr1,fr2,fr3} = 3'b000;
                        	dfr = 1'b1;
                            end
                    end
                endcase
            end
        end
                    
                
endmodule

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Verilog状态机的三段式是指状态机的Verilog写法中,将状态机的功能分为三个部分:状态转换逻辑、状态更新逻辑和输出逻辑。这种写法通常用于描述有限状态机的行为。状态转换逻辑决定下一个状态是什么,状态更新逻辑更新当前状态,而输出逻辑决定在每个状态下的输出信号。 引用提到了一段式、两段式和三段式的状态机写法。这三种写法在速度、面积和代码可维护性等方面各有优劣。其中,三段式状态机的特点是将状态转换、状态更新和输出逻辑分别独立实现,使得代码更加清晰和易于理解。 引用指出,在网络上搜索"三段式状态机"时,大多数给出的例子都是基于next_state输出的,很少看到基于current_state输出的。这导致了一种思维定势,即认为三段式状态机的第三段只能基于next_state描述,实际上这是不正确的。 根据引用中的描述,Mealy型状态机是一种常见的状态机类型,其状态变化不仅取决于当前状态,还取决于当前输入条件。输出信号不仅取决于状态,还取决于输入信号。因此,在实现Mealy型状态机时,三段式写法可以更好地描述状态转换、状态更新和输出逻辑。 总结起来,Verilog状态机的三段式是一种常见的描述有限状态机行为的写法,其中包括状态转换逻辑、状态更新逻辑和输出逻辑。这种写法能够提高代码的可读性和可维护性,并且能够灵活地适应不同类型的状态机,如Mealy型状态机。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [verilog 有限状态机的基本概念和三种写法介绍](https://blog.csdn.net/qq_39507748/article/details/108763514)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [数电和Verilog-时序逻辑实例四:状态机(三段式描述)](https://blog.csdn.net/weixin_44915807/article/details/125001040)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值