HDLBits中文版,标准参考答案 | 3.2.5 Finite State Machines | 有限状态机(3)

关注 望森FPGA  查看更多FPGA资讯

这是望森的第 18 期分享

作者 | 望森
来源 | 望森FPGA

目录

1 Serial receiver | 串行接收器

2 Serial receiver and datapath | 串行接收器和数据路径

3 Serial receiver with parity checking | 带奇偶校验的串行接收器

4 Sequence recognition | 序列识别


本文中的代码都能够正常运行,请放心食用😋~

练习的官方网站是:https://hdlbits.01xz.net/

注:作者将每个练习的知识点都放在了题目和答案之后


1 Serial receiver | 串行接收器

题目:

在许多(较旧的)串行通信协议中,每个数据字节都与起始位和停止位一起发送,以帮助接收器从位流中划定字节。一种常见的方案是使用一个起始位(0)、8 个数据位和 1 个停止位(1)。当没有传输任何内容(空闲)时,线路也处于逻辑 1。

设计一个有限状态机,当给定一个位流时,它将识别字节何时被正确接收。它需要识别起始位,等待所有 8 个数据位,然后验证停止位是否正确。如果停止位未按预期出现,则 FSM 必须等到找到停止位后才能尝试接收下一个字节。

一些时序图

无错误:

未找到停止位。第一个字节被丢弃:

答案:

1.状态转换图

2.代码

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output done
); 

    parameter IDLE        =5'b0_0001; 
    parameter START        =5'b0_0010; 
    parameter RD        =5'b0_0100; 
    parameter END        =5'b0_1000; 
    parameter ERROR        =5'b1_0000; 
    
    reg [4:0] state, next_state;
    wire IDLE2IDLE,IDLE2START,
    RD2RD,RD2END,RD2ERROR,
    END2START,END2IDLE,
    ERROR2ERROR,ERROR2IDLE;

    always @(posedge clk) begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
    
    always @(*) begin
        case(state)
            IDLE : begin
                if (IDLE2IDLE)begin
                    next_state = IDLE;
                end
                else if (IDLE2START)begin
                    next_state = START;
                end
            end
            START : begin
                next_state = RD;
            end
            RD : begin
                if (RD2RD)begin
                    next_state = RD;
                end
                else if (RD2END)begin
                    next_state = END;
                end
                else if (RD2ERROR)begin
                    next_state = ERROR;
                end
            end
            END : begin
                if (END2START)begin
                    next_state = START;
                end
                else if (END2IDLE)begin
                    next_state = IDLE;
                end
            end
            ERROR : begin
                if (ERROR2ERROR)begin
                    next_state = ERROR;
                end
                else if (ERROR2IDLE)begin
                    next_state = IDLE;
                end
            end
            default next_state = IDLE;
        endcase
    end

    //RD counter
    reg [2:0] cnt_rd;
    always @(posedge clk) begin
        if(reset)begin
            cnt_rd <= 3'd0;
        end
        else if (state == RD)begin
            cnt_rd <= cnt_rd + 1;
        end
        else begin
            cnt_rd <= 3'd0;
        end
    end
    
    assign IDLE2IDLE         = in == 1'b1;
    assign IDLE2START        = in == 1'b0;
    assign RD2RD             = cnt_rd < 3'd7;
    assign RD2END            = in == 1'b1 && cnt_rd == 3'd7;
    assign RD2ERROR          = in == 1'b0 && cnt_rd == 3'd7;
    assign END2START         = in == 1'b0;
    assign END2IDLE          = in == 1'b1;
    assign ERROR2ERROR       = in == 1'b0;
    assign ERROR2IDLE        = in == 1'b1;

    // Output logic
    assign done = state == END;

    
endmodule

2 Serial receiver and datapath | 串行接收器和数据路径

题目:

现在您有了一个可以识别何时在串行比特流中正确接收字节的有限状态机,请添加一个将输出正确接收的数据字节的数据路径。当 done 为 1 时,out_byte 需要有效,否则无需关心。

请注意,串行协议首先发送最低有效位。

一些时序图

无错误:

答案:

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output reg [7:0] out_byte,
    output done
); //

    // Use FSM from Fsm_serial
    parameter IDLE        =5'b0_0001; 
    parameter START        =5'b0_0010; 
    parameter RD        =5'b0_0100; 
    parameter END        =5'b0_1000; 
    parameter ERROR        =5'b1_0000; 
    
    reg [4:0] state, next_state;
    wire IDLE2IDLE,IDLE2START,
    RD2RD,RD2END,RD2ERROR,
    END2START,END2IDLE,
    ERROR2ERROR,ERROR2IDLE;

    always @(posedge clk) begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
    
    always @(*) begin
        case(state)
            IDLE : begin
                if (IDLE2IDLE)begin
                    next_state = IDLE;
                end
                else if (IDLE2START)begin
                    next_state = START;
                end
            end
            START : begin
                next_state = RD;
            end
            RD : begin
                if (RD2RD)begin
                    next_state = RD;
                end
                else if (RD2END)begin
                    next_state = END;
                end
                else if (RD2ERROR)begin
                    next_state = ERROR;
                end
            end
            END : begin
                if (END2START)begin
                    next_state = START;
                end
                else if (END2IDLE)begin
                    next_state = IDLE;
                end
            end
            ERROR : begin
                if (ERROR2ERROR)begin
                    next_state = ERROR;
                end
                else if (ERROR2IDLE)begin
                    next_state = IDLE;
                end
            end
            default next_state = IDLE;
        endcase
    end

    //RD counter
    reg [2:0] cnt_rd;
    always @(posedge clk) begin
        if(reset)begin
            cnt_rd <= 3'd0;
        end
        else if (state == RD)begin
            cnt_rd <= cnt_rd + 1;
        end
        else begin
            cnt_rd <= 3'd0;
        end
    end
    
    assign IDLE2IDLE         = in == 1'b1;
    assign IDLE2START        = in == 1'b0;
    assign RD2RD             = cnt_rd < 3'd7;
    assign RD2END            = in == 1'b1 && cnt_rd == 3'd7;
    assign RD2ERROR          = in == 1'b0 && cnt_rd == 3'd7;
    assign END2START         = in == 1'b0;
    assign END2IDLE          = in == 1'b1;
    assign ERROR2ERROR       = in == 1'b0;
    assign ERROR2IDLE        = in == 1'b1;

    // Output logic
    assign done = state == END;
    
    // New: Datapath to latch input bits.
    reg [7:0] byte_reg;
    always @(posedge clk) begin
        if(state == START)begin
            byte_reg <= {in,byte_reg[7:1]};
        end
        else if(state == RD && cnt_rd < 3'd7)begin
                byte_reg <= {in,byte_reg[7:1]};
        end
        else begin
            byte_reg <= byte_reg;
        end
    end
    
    always @(*) begin
        if(done)begin
                out_byte = byte_reg;
        end
        else begin
            out_byte = 8'd0;
        end
    end

endmodule

知识点:

提示:

串行比特流需要一次移入一位,然后并行读出。

注意:进入start状态后就开始寄存输入数据。


3 Serial receiver with parity checking | 带奇偶校验的串行接收器

题目:

我们想为串行接收器添加奇偶校验。奇偶校验在每个数据字节后添加一个额外的位。我们将使用奇校验,其中接收的 9 位中的 1 的数量必须是奇数。例如,101001011 满足奇校验(有 5 个 1),但 001001011 不满足。

更改您的 FSM 和数据路径以执行奇校验。仅当正确接收字节并且其奇偶校验通过时才断言完成信号。与串行接收器 FSM 一样,此 FSM 需要识别起始位,等待所有 9 个(数据和奇偶校验)位,然后验证停止位是否正确。如果停止位未在预期时出现,则 FSM 必须等到找到停止位后才能尝试接收下一个字节。

为您提供了以下模块,可用于计算输入流的奇偶校验(它是带重置的 TFF)。预期用途是给予它输入位流,并在适当的时间重置,以便它计算每个字节中 1 的数量。

module parity (
    input clk,
    input reset,
    input in,
    output reg odd);

    always @(posedge clk)
        if (reset) odd <= 0;
        else if (in) odd <= ~odd;

endmodule

请注意,串行协议首先发送最低有效位,然后在 8 个数据位之后发送奇偶校验位。

一些时序图

无帧错误:

奇校验通过第一个字节,但未通过第二个字节。

答案:

1.状态转换图

2.代码

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output [7:0] out_byte,
    output done
); //

    // Modify FSM and datapath from Fsm_serialdata
    // Use FSM from Fsm_serial
    parameter IDLE        =6'b00_0001; 
    parameter START       =6'b00_0010; 
    parameter RD          =6'b00_0100; 
    parameter CHECK       =6'b00_1000; 
    parameter END         =6'b01_0000; 
    parameter ERROR       =6'b10_0000; 
    
    reg [5:0] state, next_state;
    wire IDLE2IDLE,IDLE2START,
    RD2RD,RD2CHECK,
    CHECK2ERROR,CHECK2END,
    END2START,END2IDLE,
    ERROR2ERROR,ERROR2IDLE;
    reg odd_check;

    always @(posedge clk) begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
    
    always @(*) begin
        case(state)
            IDLE : begin
                if (IDLE2IDLE)begin
                    next_state = IDLE;
                end
                else if (IDLE2START)begin
                    next_state = START;
                end
            end
            START : begin
                next_state = RD;
            end
            RD : begin
                if (RD2RD)begin
                    next_state = RD;
                end
                else if (RD2CHECK)begin
                    next_state = CHECK;
                end
            end
            CHECK : begin
                if (CHECK2ERROR)begin
                    next_state = ERROR;
                end
                else if (CHECK2END)begin
                    next_state = END;
                end
            end
            END : begin
                if (END2START)begin
                    next_state = START;
                end
                else if (END2IDLE)begin
                    next_state = IDLE;
                end
            end
            ERROR : begin
                if (ERROR2ERROR)begin
                    next_state = ERROR;
                end
                else if (ERROR2IDLE)begin
                    next_state = IDLE;
                end
            end
            default next_state = IDLE;
        endcase
    end

    //RD counter
    reg [2:0] cnt_rd;
    always @(posedge clk) begin
        if(reset)begin
            cnt_rd <= 3'd0;
        end
        else if (state == RD)begin
            cnt_rd <= cnt_rd + 1;
        end
        else begin
            cnt_rd <= 3'd0;
        end
    end
    
    assign IDLE2IDLE         = in == 1'b1;
    assign IDLE2START        = in == 1'b0;
    assign RD2RD             = cnt_rd < 3'd7;
    assign RD2CHECK          = cnt_rd == 3'd7;
    assign CHECK2ERROR       = in == 1'b0;
    assign CHECK2END         = in == 1'b1;
    assign END2START         = in == 1'b0;
    assign END2IDLE          = in == 1'b1;
    assign ERROR2ERROR       = in == 1'b0;
    assign ERROR2IDLE        = in == 1'b1;

    // Output logic
    assign done = state == END && odd_check;
    
    // 寄存进入RD状态的前8位数据
    reg [7:0] byte_reg;
    always @(posedge clk) begin
        if(state == START)begin
            byte_reg <= {in,byte_reg[7:1]};
        end
        else if(state == RD && cnt_rd < 3'd7)begin
            byte_reg <= {in,byte_reg[7:1]};
        end
        else begin
            byte_reg <= byte_reg;
        end
    end
    
    //done == 1时输出寄存数据
    always @(*) begin
        if(done)begin
            out_byte = byte_reg;
        end
        else begin
            out_byte = 8'd0;
        end
    end
    
    //奇偶校验例化
    wire    reset_parity,odd;
    parity parity_inst(
        .clk(clk),
        .reset(reset_parity || reset),
            .in(in),
            .odd(odd)
    );
    
    //奇偶校验复位
    always @(*) begin
        if(next_state == START)begin
            reset_parity = 1'b1;
        end
        else begin
            reset_parity = 1'b0;
        end
    end
           
    //奇偶校验结果
    always @(*) begin
        if(next_state == CHECK)begin
            odd_check = odd;
        end
        else begin
            odd_check = odd_check;
        end
    end
endmodule

知识点:

重点在于控制好奇偶校验的开始时间和结束时间。


4 Sequence recognition | 序列识别

题目:

“同步 HDLC 帧结构”(Synchronous HDLC framing)涉及解码连续的数据位流,以查找指示帧(数据包)开始和结束的位模式。

看到正好 6 个连续的 1(即 01111110)是指示帧边界的“标志”。为了避免数据流意外包含“标志”,发送方会在每 5 个连续的 1 后插入一个零,接收方必须检测并丢弃该零。如果有 7 个或更多连续的 1,我们还需要发出错误信号。

创建一个有限状态机来识别这三个序列:

0111110:发出需要丢弃一个位的信号(disc)。

01111110:标记帧的开始/结束(flag)。

01111111...:错误(7 个或更多 1)(err)。

当 FSM 重置时,它应该处于一种状态,就像之前的输入是 0 一样。

以下是一些示例序列,说明了所需的操作。

丢弃 0111110:

标记 01111110:

重置行为和错误 01111111...:

实现这个状态机。

答案:

1.状态转换图

2.代码

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

    parameter IDLE    =10'b00_0000_0001; 
    parameter S1      =10'b00_0000_0010;
    parameter S2      =10'b00_0000_0100;
    parameter S3      =10'b00_0000_1000;
    parameter S4      =10'b00_0001_0000;
    parameter S5      =10'b00_0010_0000;
    parameter S6      =10'b00_0100_0000;
    parameter DISC    =10'b00_1000_0000;
    parameter FLAG    =10'b01_0000_0000;
    parameter ERR     =10'b10_0000_0000;
    
    reg [9:0] state, next_state;
    
    wire IDLE2IDLE,IDLE2S1;
    wire S12IDLE,S12S2;
    wire S22IDLE,S22S3;
    wire S32IDLE,S32S4;
    wire S42IDLE,S42S5;
    wire S52DISC,S52S6;
    wire S62FLAG,S62ERR;
    wire DISC2IDLE,DISC2S1;
    wire FLAG2IDLE,FLAG2S1;
    wire ERR2IDLE,ERR2ERR;

    always @(posedge clk) begin
        if(reset)begin
            state <= IDLE;
        end
        else begin
            state <= next_state;
        end
    end
    
    always @(*) begin
        case(state)
            IDLE : begin
                if (IDLE2IDLE)begin
                    next_state = IDLE;
                end
                else if(IDLE2S1)begin
                    next_state = S1;
                end
            end
            S1 : begin
                if (S12IDLE)begin
                    next_state = IDLE;
                end
                else if(S12S2)begin
                    next_state = S2;
                end
            end
            S2 : begin
                if (S22IDLE)begin
                    next_state = IDLE;
                end
                else if(S22S3)begin
                    next_state = S3;
                end
            end
            S3 : begin
                if (S32IDLE)begin
                    next_state = IDLE;
                end
                else if(S32S4)begin
                    next_state = S4;
                end
            end
            S4 : begin
                if (S42IDLE)begin
                    next_state = IDLE;
                end
                else if(S42S5)begin
                    next_state = S5;
                end
            end
            S5 : begin
                if (S52DISC)begin
                    next_state = DISC;
                end
                else if(S52S6)begin
                    next_state = S6;
                end
            end
            S6 : begin
                if (S62FLAG)begin
                    next_state = FLAG;
                end
                else if(S62ERR)begin
                    next_state = ERR;
                end
            end
            DISC : begin
                if (DISC2IDLE)begin
                    next_state = IDLE;
                end
                else if(DISC2S1)begin
                    next_state = S1;
                end
            end
            FLAG : begin
                if (FLAG2IDLE)begin
                    next_state = IDLE;
                end
                else if(FLAG2S1)begin
                    next_state = S1;
                end
            end
            ERR : begin
                if (ERR2IDLE)begin
                    next_state = IDLE;
                end
                else if(ERR2ERR)begin
                    next_state = ERR;
                end
            end
            default next_state = IDLE;
        endcase
    end
    
    assign IDLE2IDLE   = in == 0;
    assign IDLE2S1     = in == 1;
    assign S12IDLE     = in == 0;
    assign S12S2       = in == 1;
    assign S22IDLE     = in == 0;
    assign S22S3       = in == 1;
    assign S32IDLE     = in == 0;
    assign S32S4       = in == 1;
    assign S42IDLE     = in == 0;
    assign S42S5       = in == 1;
    assign S52DISC     = in == 0;
    assign S52S6       = in == 1;
    assign S62FLAG     = in == 0;
    assign S62ERR      = in == 1;
    assign DISC2IDLE   = in == 0;
    assign DISC2S1     = in == 1;
    assign FLAG2IDLE   = in == 0;
    assign FLAG2S1     = in == 1;
    assign ERR2IDLE    = in == 0;
    assign ERR2ERR     = in == 1;

    // Output logic
    assign disc     = state == DISC;
    assign flag     = state == FLAG;
    assign err      = state == ERR;
    
endmodule

知识点:

提示:

使用具有大约 10 个状态的 Moore 状态机。


- END -

公z号/CSDN搜索【望森FPGA】,查看更多FPGA资讯~

相关推荐文章,点击跳转:

望森FPGA的HDLBits合集

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

望森FPGA

谢谢你的支持,这对我很重要~

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

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

打赏作者

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

抵扣说明:

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

余额充值