HDLBits-Circuits学习小结(九)有限状态机进阶(Serial receiver、sequence recognition)

1 串行通信

1.1 serial receiver

在许多(较旧的)串行通信协议中,每个数据字节都与起始位和停止位一起发送,以帮助接收器从位流中分隔字节。一种常见的方案是使用一个起始位(0),8个数据位和1个停止位(1)。当没有任何传输(空闲)时,线路也处于逻辑1。
设计一个有限状态机,当给定比特流时,它将识别何时正确接收了字节。它需要标识起始位,等待所有8个数据位,然后验证停止位是否正确。
如果停止位没有出现在预期的位置,FSM必须等待直到找到停止位,然后才能尝试接收下一个字节。

无错误的情况:
无错误的情况
未找到停止位。第一个字节被丢弃:

未找到停止位

module top_module(
    input clk,
    input in,
    input reset,    // Synchronous reset
    output done
); 
    parameter start=0,data1=1,data2=2,data3=3,data4=4,data5=5,
    data6=6,data7=7,data8=8,waitup=9,stop=10,idle=11;
    reg [3:0] state,next;
    
    always @(*) begin
        case(state)
            idle  : next <= in ? idle : start;
            start : next <= data1;
            data1 : next <= data2;
            data2 : next <= data3;
            data3 : next <= data4;
            data4 : next <= data5;
            data5 : next <= data6;
            data6 : next <= data7;
            data7 : next <= data8;
            data8 : next <= in ? stop : waitup;
            waitup: next <= in ? idle : waitup;
            stop  : next <= in ? idle : start;
            default: next <= idle;
        endcase
    end
    
    always @(posedge clk) begin
        if(reset)
            state <= idle;
        else
            state <= next;
    end
    
    assign done = (state==stop);

endmodule

1.2 serial receiver and datapath

既然您已经有了一个有限状态机,可以确定何时在串行位流中正确接收了字节,那么添加一个数据路径,它将输出正确接收的数据字节。当完成为1时,out_byte需要有效,否则不在乎。
请注意,串行协议首先发送最低有效位。

1.2

提示:串行位流需要一次移位一位,然后并行读出。

这里的解决方法与这篇文章中的PS/2 packet parser datapath部分的思路一致。均是利用移位寄存器进行存储数据。

solution:

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

    // Use FSM from Fsm_serial
    parameter start=0,data1=1,data2=2,data3=3,data4=4,data5=5,
    data6=6,data7=7,data8=8,waitup=9,stop=10,idle=11;
    reg [3:0] state,next;
    reg [7:0] data;
    
    always @(*) begin
        case(state)
            idle  : next <= in ? idle : start;
            start : next <= data1;
            data1 : next <= data2;
            data2 : next <= data3;
            data3 : next <= data4;
            data4 : next <= data5;
            data5 : next <= data6;
            data6 : next <= data7;
            data7 : next <= data8;
            data8 : next <= in ? stop : waitup;
            waitup: next <= in ? idle : waitup;
            stop  : next <= in ? idle : start;
            default: next <= idle;
        endcase
    end
    
    always @(posedge clk) begin
        if(reset)
            state <= idle;
        else
            state <= next;
    end
    
    assign done = (state==stop);

    // New: Add parity checking.
    always @(posedge clk) begin
        if (reset) begin
            data <= 8'd0;
        end
        else begin
            if (next == data1 || next == data2 || next == data3 || next == data4 || next == data5 || next == data6 || next == data7 || next == data8) begin
                data <= {in, data[7:1]};//最后一个进的要在最高位的话,那就in在前
                //最后一个进的要在最低位的话,那就in在后,同时data[7:1]要改为data[6:0]
            end
        end
    end
    
    assign out_byte = done ? data : 8'd0;

endmodule

1.3 serial receiver with parity checking

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

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

1.3

提供了以下模块,该模块可用于计算输入流的奇偶校验(这是带复位的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个数据位之后发送奇偶校验位。

参考这篇文章.

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
    parameter idle = 4'd0, start = 4'd1, trans0 = 4'd2,trans1 = 4'd3, trans2 = 4'd4, trans3 = 4'd5;
    parameter trans4 = 4'd6, trans5 = 4'd7, trans6 = 4'd8, trans7 = 4'd9, stop = 4'd10, err = 4'd11, pari = 4'd12;
    reg [3:0] state, next_state;
    reg [7:0] data;
    wire odd, reset_p;
    reg done_reg;

    always @(*) begin
        case (state)
            idle:   next_state <= in ? idle : start;
            start:  next_state <= trans0;
            trans0: next_state <= trans1;
            trans1: next_state <= trans2;
            trans2: next_state <= trans3;
            trans3: next_state <= trans4;
            trans4: next_state <= trans5;
            trans5: next_state <= trans6;
            trans6: next_state <= trans7;
            trans7: next_state <= pari;
            pari:   next_state <= in ? idle : err; //如果停止位没有出现在预期的位置,FSM必须等待直到找到停止位,然后才能尝试接收下一个字节。

            err:    next_state <= in ? idle : err;
        endcase
    end

    always @(posedge clk) begin
        if (reset) begin
            state <= idle;
        end
        else begin
            state <= next_state;
        end
    end

    // New: Add parity checking.
    always @(posedge clk) begin
        if (reset) begin
            data <= 8'd0;
            reset_p <= 1'b1;
            done_reg <= 1'b0;
        end
        else begin
            if (next_state == trans0 || next_state == trans1 || next_state == trans2 || next_state == trans3 || next_state == trans4 || next_state == trans5 || next_state == trans6 || next_state == trans7) begin
                data <= {in, data[7:1]};
            end
            else if (next_state == start) begin
                data <= 8'd0;
                reset_p <= 1'b0;
                done_reg <= 1'b0;
            end
            else if (next_state == idle) begin
                done_reg <= odd;
            end
            else if (next_state == pari) begin
                reset_p <= 1'b1;
            end
        end
    end

    assign done = done_reg;
    assign out_byte = done ? data : 8'd0;
    parity par_mod(clk, reset | reset_p, in, odd);

endmodule

2 sequence recognition

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

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

0111110:信号位需要丢弃(disc)。
01111110:标记帧的开始/结束(flag)。
01111111 …:错误(7或更多1s)(err)。
重置FSM时,其状态应类似于先前的输入为0。

以下是一些示例序列,它们说明了所期望的操作。
Discard 0111110:
a

Flag 01111110:

b

Reset behaviour and error 01111111…:

c

提示:
使用一个大约有10个状态的莫尔型状态机。

d

module top_module(
    input clk,
    input reset,    // Synchronous reset
    input in,
    output disc,
    output flag,
    output err);
    
    parameter none=0,one=1,two=2,three=3,four=4,five=5,six=6,error=7,discard=8,f=9;
    reg [3:0] state,next;
    
    always @(*) begin
        case(state)
            none: next = in ? one : none;
            one : next = in ? two : none;
            two : next = in ? three:none;
            three:next = in ? four :none;
            four: next = in ? five: none;
            five: next = in ? six : discard;
            six : next = in ? error : f;
            error:next = in ? error : none;
            discard:next = in ? one : none;
            f: next = in ? one : none;
        endcase
    end
    
    always @(posedge clk) begin
        if(reset)
            state <= none;
        else
        	state <= next;
    end
    
    assign disc = (state==discard);
    assign flag = (state==f);
    assign err = (state==error);
        
endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值