HDLBits Verilog编程题136\137 串行数据接收状态机

串行数据接收状态机

  • 136:串行数据接收Serial receiver and datapath(fsm_serialdata)
  • 137:奇校验串行数据接收Serial receiver with parity checking(Fsm serialdp)

前一道题(135.Serial receiver),不用记录数据,只输出接收完成标志done,只要把136中数据存储输出部分删除即可,因此这里不再赘述。

136:串行数据接收Serial receiver and datapath(fsm_serialdata)

原题链接:https://hdlbits.01xz.net/wiki/Fsm_serialdata
题目简单说明:接收数据格式为:1bit起始位(0)+8bit数据位+1bit停止位(1);若停止位为0则数据传输出错。
状态转换如下图:
HDLBits Verilog编程题136\137 串行数据接收状态机_第1张图片

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

    reg [3:0] state_c, state_n;
    parameter Idle=0, Recv=1, Done=2, Error=3, Stop=4;
    wire Idle2Recv, Recv2Stop, Stop2Error, Stop2Done, Done2Recv, Done2Idle, Error2Idle;
    wire cnt_add, cnt_end;
    reg [3:0] cnt;
    reg [7:0] temp_data;
    
    always@(posedge clk) begin
        if(reset)
            cnt <= 0;
        else if(cnt_add) begin
            if(cnt_end)
                cnt <= 0;
            else 
                cnt <= cnt+1;
        end
    end
    assign cnt_add = state_c==Recv;
    assign cnt_end = cnt_add && cnt==8-1;
    
    always@(posedge clk) begin
        if(reset)
            state_c <= Idle;
        else
            state_c <= state_n;
    end
    
    always@(*) begin
        case (state_c)
            Idle:begin
                if(Idle2Recv)
                    state_n = Recv;
                else
                    state_n = state_c;
            end
            Recv:begin
                if(Recv2Stop)
                    state_n = Stop;
                else 
                    state_n = state_c;
            end
            Stop:begin
                if(Stop2Done)
                    state_n = Done;
                else if(Stop2Error)
                    state_n = Error;
                else 
                    state_n = state_c;
            end
            Done:begin
                if(Done2Recv)
                    state_n = Recv;
                else if(Done2Idle)
                    state_n = Idle;
                else 
                    state_n = state_c;
            end
            Error:begin
                if(Error2Idle)
                    state_n = Idle;
                else 
                    state_n = state_c;
            end
            default:state_n = Idle;
        endcase
    end
    
    assign Idle2Recv = state_c==Idle && in==0;
    assign Recv2Stop = state_c==Recv && cnt==8-1;
    assign Stop2Done = state_c==Stop && in==1;
    assign Stop2Error = state_c==Stop && in==0;
    assign Done2Recv = state_c==Done && in==0;
    assign Done2Idle = state_c==Done && in==1;
    assign Error2Idle = state_c==Error && in==1;
    
    assign done = state_c==Done;
    
    always@(posedge clk) begin
        if(reset)
            temp_data<=8'h00;
        else if(state_c == Recv)
            temp_data[cnt] = in;
    end
    assign out_byte = (state_c==Done)?temp_data:8'hzz;

endmodule

数据接收部分使用一个计数器,在接收状态时(state_c=Recv)对cnt计数。

137:奇校验串行数据接收Serial receiver with parity checking(Fsm serialdp)

原题链接:https://hdlbits.01xz.net/wiki/Fsm_serialdp
题目简单说明:较上一题多了奇校验部分;接收完数据位和奇校验位后会等待接收停止位,若没有接收到停止位,状态机在接收下一数据时会一直等待接收停止位;奇校验使用题目中提供的模块parity :

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

注意:parity模块的复位信号是组合的;输入信号in只用在Recv接收数据状态下有效;Wait状态是必需的。
状态转换如下图:HDLBits Verilog编程题136\137 串行数据接收状态机_第2张图片

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

    reg [3:0] state_c, state_n;
    parameter Idle=0, Recv=1, Done=2, Error=3, Stop=4, Wait=5;
    wire Idle2Recv, Recv2Stop, Stop2Error, Stop2Done, Done2Recv, Done2Idle, Error2Idle;
    wire Error2Recv, Stop2Wait, Wait2Idle;
    wire cnt_add, cnt_end;
    reg [3:0] cnt;
    reg [8:0] temp_data;
    
    always@(posedge clk) begin
        if(reset)
            cnt <= 0;
        else if(cnt_add) begin
            if(cnt_end)
                cnt <= 0;
            else 
                cnt <= cnt+1;
        end
    end
    assign cnt_add = state_c==Recv;
    assign cnt_end = cnt_add && cnt==9-1;
    
    always@(posedge clk) begin
        if(reset)
            state_c <= Idle;
        else
            state_c <= state_n;
    end
    
    always@(*) begin
        case (state_c)
            Idle:begin
                if(Idle2Recv)
                    state_n = Recv;
                else
                    state_n = state_c;
            end
            Recv:begin
                if(Recv2Stop)
                    state_n = Stop;
                else 
                    state_n = state_c;
            end
            Stop:begin
                if(Stop2Done)
                    state_n = Done;
                else if(Stop2Error)
                    state_n = Error;
                else if(Stop2Wait)
                    state_n = Wait;
                else
                    state_n = state_c;
            end
            Done:begin
                if(Done2Recv)
                    state_n = Recv;
                else if(Done2Idle)
                    state_n = Idle;
                else 
                    state_n = state_c;
            end
            Error:begin
                if(Error2Idle)
                    state_n = Idle;
                else if(Error2Recv)
                    state_n = Recv;
                else 
                    state_n = state_c;
            end
            Wait:begin
                if(Wait2Idle)
                    state_n = Idle;
                else
                    state_n = state_c;
            end
            default:state_n = Idle;
        endcase
    end
    
    assign Idle2Recv = state_c==Idle && in==0;
    assign Recv2Stop = state_c==Recv && cnt==9-1;
    assign Stop2Done = state_c==Stop && in==1 && odd;
    assign Stop2Error = state_c==Stop && in==1 && ~odd;
    assign Done2Recv = state_c==Done && in==0;
    assign Done2Idle = state_c==Done && in==1;
    assign Error2Idle = state_c==Error && in==1;
    assign Error2Recv = state_c==Error && in==0;
    assign Stop2Wait = state_c==Stop && in==0;
    assign Wait2Idle = state_c==Wait && in==1;
    
    assign done = state_c==Done;
    
    always@(posedge clk) begin
        if(reset)
            temp_data<=9'h000;
        else if(state_c == Recv)
            temp_data[cnt] = in;
    end
    assign out_byte = (done==1)?temp_data[7:0]:8'hzz;
    
    wire odd, parity_in,parity_reset;
    assign parity_in = state_c==Recv && in;
    assign parity_reset = state_c==Idle || state_c==Error || state_c==Done || reset;
    parity U1(.clk(clk),
              .reset(parity_reset),
              .in(parity_in),
              .odd(odd)
             );

endmodule

这里奇校验接收和数据接收部分放在了一个状态,因此接收计数cnt=9-1(接收9位数据)。
状态机采用三段式,输出部分使用了组合电路;为代码直观易于理解规范,转换条件单独使用wire类型列出。

你可能感兴趣的:(Verilog)