HDLBits答案(19)_Verilog有限状态机(6)

本文介绍了使用Verilog实现两种类型的状态机:一种用于同步帧检测,识别特定的位模式,另一种用于串行补码转换。在帧检测中,状态机识别帧边界和错误条件,而在补码转换中,状态机处理二进制补码的取反和加1操作。文中提供了两种不同的设计方案,并附带了状态转移图和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Verilog有限状态机(6)

HDLBits链接


前言

今天继续更新状态机小节的习题。


题库

Fsm hdlc

同步帧检测涉及对数据的连续位流进行解码,以寻找指示帧(数据包)开始和结束的位模式。 6个连续的1(即01111110)是表示帧边界的“标志”。 为了避免数据流意外包含“标志”,发送方必须在接收方必须检测并丢弃的每5个连续的1秒后插入一个零。 如果连续7个或更多1,我们还需要发出错误信号。

可以通过状态机来识别下面三种序列:

0111110:表示5个1后面的0bit需被忽略;

01111110:表示一帧的开始或结束;

01111111…:错误

当状态机被复位时,它应当表现为之前的输入为0;

下面是三种波形示例:

pic1.png

pic2.png

pic3.png

官方提供的状态机设计提示:

pic4.png

Solution:

//----------------way1----------------------
module top_module(
    input clk,
    input reset,    // Synchronous reset
    input in,
    output disc,
    output flag,
    output err);

    parameter NONE = 4'd0,ONE = 4'd1,TWO = 4'd2;
    parameter THREE = 4'd3,FOUR = 4'd4,FIVE = 4'd5;
    parameter SIX = 4'd6,ERROR = 4'd7;
    parameter DISC = 4'd8,FLAG = 4'd9;

    reg [3:0] current_state,next_state;

    always @(*) begin
        case(current_state)
            NONE:begin
                next_state = in ? ONE : NONE;
            end
            ONE:begin
                next_state = in ? TWO : NONE;
            end
            TWO:begin
                next_state = in ? THREE : NONE;
            end
            THREE:begin
                next_state = in ? FOUR : NONE;
            end
            FOUR:begin
                next_state = in ? FIVE : NONE;
            end
            FIVE:begin
                next_state = in ? SIX : DISC;
            end
            SIX:begin
                next_state = in ? ERROR : FLAG;
            end
            DISC:begin
                next_state = in ? ONE : NONE;
            end
            FLAG:begin
                next_state = in ? ONE : NONE;
            end
            ERROR:begin
                next_state = in ? ERROR : NONE;
            end
        endcase
    end

    always @(posedge clk) begin
        if(reset)begin
            current_state <= NONE;
        end
        else begin
            current_state <= next_state;
        end
    end

    always @(posedge clk) begin
        if(reset)begin
            disc <= 1'd0;
            flag <= 1'd0;
            err  <= 1'd0;
        end
        else begin
            case(next_state)
                DISC:begin
                    disc <= 1'd1;
                    flag <= 1'd0;
                    err  <= 1'd0;
                end
                FLAG:begin
                    disc <= 1'd0;
                    flag <= 1'd1;
                    err  <= 1'd0;
                end
                ERROR:begin
                    disc <= 1'd0;
                    flag <= 1'd0;
                    err  <= 1'd1;
                end
                default:begin
                    disc <= 1'd0;
                    flag <= 1'd0;
                    err  <= 1'd0;
                end
            endcase
        end
    end

endmodule
//----------------way2----------------------
module top_module(
    input clk,
    input reset,    // Synchronous reset
    input in,
    output disc,
    output flag,
    output err);

    parameter NONE = 3'd0,DATA = 3'd1;
    parameter DISC = 3'd2,FLAG = 3'd3,ERROR = 3'd4;

    reg [2:0] current_state,next_state;
    reg [2:0] counter;

    always @(*) begin
        case(current_state)
            NONE:begin
                next_state = in ? DATA : NONE;
            end
            DATA:begin
                case(counter)
                    3'd5:   next_state = in ? DATA : DISC;
                    3'd6:   next_state = in ? ERROR : FLAG;
                    default:next_state = in ? DATA : NONE;
                endcase
            end
            DISC:begin
                next_state = in ? DATA : NONE;
            end
            FLAG:begin
                next_state = in ? DATA : NONE;
            end
            ERROR:begin
                next_state = in ? ERROR : NONE;
            end
        endcase
    end

    always @(posedge clk) begin
        if(reset)begin
            current_state <= NONE;
        end
        else begin
            current_state <= next_state;
        end
    end

    always @(posedge clk) begin
        if(reset)begin
            disc <= 1'd0;
            flag <= 1'd0;
            err  <= 1'd0;
            counter <= 3'd0;
        end
        else begin
            case(next_state)
                DATA:begin
                    disc <= 1'd0;
                    flag <= 1'd0;
                    err  <= 1'd0;
                    counter <= counter + 1'd1;
                end
                DISC:begin
                    disc <= 1'd1;
                    flag <= 1'd0;
                    err  <= 1'd0;
                    counter <= 3'd0;
                end
                FLAG:begin
                    disc <= 1'd0;
                    flag <= 1'd1;
                    err  <= 1'd0;
                    counter <= 3'd0;
                end
                ERROR:begin
                    disc <= 1'd0;
                    flag <= 1'd0;
                    err  <= 1'd1;
                    counter <= 3'd0;
                end
                default:begin
                    disc <= 1'd0;
                    flag <= 1'd0;
                    err  <= 1'd0;
                    counter <= 3'd0;
                end
            endcase
        end
    end

endmodule

其中way1与题目提示的思路相同,其中第三段状态机一开始忘记加reset的那种情况,果然还是长时间没接触verilog手生了,以后继续练习;

way2使用counter去掉中间状态,靠输入和counter的值来决定状态转移,是一种米利状态机的思想;状态转移图如下(手绘有点丑,见谅):

pic5.jpg

米利型状态机

检测输入的X中”101“是否出现,出现的话输出Z为1,否则为0。复位为异步低电平复位;只允许出现3种状态;允许交叠检测:即输入若为10101时,Z应该在时刻3和时刻5各输出一次1;

代码中主要妙在两处:一是如何用3种状态表示,需在第三种状态中将输出与输入关联起来;二是如何进行交叠检测,状态的转移有妙处;

Solution

module top_module (
    input clk,
    input aresetn,    // Asynchronous active-low reset
    input x,
    output z ); 

    parameter S0 = 2'd0,S1 = 2'd1,S2 = 2'd2;
    reg [1:0] current_state,next_state;

    always @(*) begin
        case(current_state)
            S0: next_state = x ? S1 : S0;
            S1: next_state = x ? S1 : S2;
            S2: next_state = x ? S1 : S0;
        endcase
    end

    always @(posedge clk or negedge aresetn) begin
        if(~aresetn)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end

    always @(*) begin
        z = (current_state == S2) ? x : 1'b0;
    end

endmodule

Q5a:Serial two’s complementer(Moore FSM)

pic6.png

作者这里假设我们这边输入的都是负数,不用管符号位的问题;即补码全部都是取反加1求得。

以上图为例,输入的左边为低位数据,右边为高位数据;即输入为00110100,则取反加1后得输出为11001100;

取反操作好进行,主要麻烦在加一的操作上,不知道进位到哪一位为止,此时我们用状态机来解决;若最前面的输入都是0的话,取反均为1,低位加1的时候一直进位,则输出都是0,直到输入有个1为止(取反加1不进位),这一阶段我们用一个状态S0来表示;后面阶段就将输入取反进行输出即可,因为进位链在S0状态已结束;

因为是摩尔型状态机,输出的结果与输入无关,仅与状态有关。所以我们这里用到3个状态,代码如下所示:

Solution

module top_module (
    input clk,
    input areset,
    input x,
    output z
); 

    parameter S0 = 2'd0, S1 = 2'd1, S2 = 2'd2;
    reg [1:0] current_state, next_state;

    always @(*) begin
        case(current_state)
            S0:     next_state = x ? S1 : S0;
            S1:     next_state = x ? S2 : S1;
            S2:     next_state = x ? S2 : S1;
        endcase
    end

    always @(posedge clk or posedge areset) begin
        if(areset)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end

    assign z = (current_state == S1);

endmodule

Q5b:Serial two’s complementer(Mealy FSM)

题意与上题相同,此时用米利型状态机实现。下面是官方提供的状态转移提示:

pic7.png

module top_module (
    input clk,
    input areset,
    input x,
    output z
); 

    parameter S0 = 1'b0, S1 = 1'b1;
    reg current_state,next_state;

    always @(*) begin
        case(current_state)
            S0:     next_state = x ? S1 : S0;
            S1:     next_state = S1;
        endcase
    end

    always @(posedge clk or posedge areset) begin
        if(areset)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end

    assign z = ((current_state == S0) && x) || ((current_state == S1) && ~x);

endmodule

结语

今天先更新这几题吧,大家如果对转补码的题目还有什么疑问欢迎评论交流,代码有不足之处还望指正。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日拱一卒_未来可期

若复习顺利望有闲钱的同学支持下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值