【FPGA】使用状态机完成对控制报文和数据报文的读取

一、项目需求

在这里插入图片描述

二、状态机设计

初始状态IDLE,检测到报文头进入TYPE,检测到控制报文类型进入控制报CONTROL,然后计数器记满64同时输出64个数,进入FCS,计数器记满4个数同时输出4个数后回到IDLE。如果在TYPE检测到数据报文类型进入长度LEN,计数器记满,然后输出len个数据和4个FCS回到IDLE。如果进入TYPE后没有检测到任何报文类型,回到IDLE。

在这里插入图片描述

三、代码设计

运用了三段状态机,共有六个状态和七个状态转移。
IDLE>TYPE的状态转移和长度有两个数,用了对第一个数进行打拍然后对两个数进行与和拼接运算。
IDLE的状态转移直接判断din输入的数据。
其他状态转移均可用计数器。

代码如下:

module  exam(
    input           clk         ,
    input           rst_n       ,
    input   [7:0]   din         ,
    output reg [7:0]   dout        ,
    output     reg     dout_vld    
);

localparam  
	IDLE    = 6'b000001,		
	TYPE    = 6'b000010,		
	CONTROL = 6'b000100,		
	LEN     = 6'b001000,
    DATA    = 6'b010000,
    FCS     = 6'b100000;		

reg     [5:0]   state_c ;
reg     [5:0]   state_n ;
wire    IDLE_TYPE   ;
wire    TYPE_CONTROL;
wire    TYPE_LEN   ;
wire    LEN_DATA    ;
wire    CONTROL_FCS ;
wire    DATA_FCS     ; 
wire    FCS_IDLE    ;

wire    head_flag1;//判断报文头第一位
wire    head_flag2;//判断报文头第二位
reg     head_flag3;

assign  head_flag1 = (din == 8'h55) ;
assign  head_flag2 = (din == 8'hd5) ;

//head_flag1打拍得到head_flag3
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        head_flag3 <= 1'b0 ;
    else
        head_flag3 <= head_flag1;
end

reg     [6:0]   control_cnt ;
reg     [1:0]   len_cnt ;
reg     [15:0]  data_cnt ;
reg     [3:0]   fcs_cnt ;
reg     [7:0]   len1 ;
reg     [7:0]   len2 ;
reg     [7:0]   len3 ;
reg     [15:0]  len0 ;
reg     [15:0]  len_fin ;


//控制报计数到64bit
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        control_cnt <= 1'b0;
    else if(state_c == CONTROL)begin
        if(control_cnt == 63)
            control_cnt <= 1'b0 ;
        else
            control_cnt <= control_cnt+1 ;
    end
    else
        control_cnt <= 1'b0 ;
end

//数据报计数到lenbit
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        data_cnt <= 1'b0;
    else if(state_c == DATA)begin
        if(data_cnt == len_fin-1)
            data_cnt <= 1'b0 ;
        else
            data_cnt <= data_cnt+1 ;
    end
    else
        data_cnt <= 1'b0 ;
end

//FCS计数到4bit
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        fcs_cnt <= 1'b0;
    else if(state_c == FCS)begin
        if(fcs_cnt == 3)
            fcs_cnt <= 1'b0 ;
        else
            fcs_cnt <= fcs_cnt+1 ;
    end
    else
        fcs_cnt <= 1'b0 ;
end

//len计数到2bit
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        len_cnt <= 1'b0 ;
    else if(state_c == LEN)begin
        if(len_cnt == 1)
            len_cnt <= 1'b0 ;
        else
            len_cnt <= len_cnt+1 ;
    end
    else
        len_cnt <= 1'b0 ;
end

//len第一位
always @(*) begin
    if(state_c == LEN && len_cnt == 0)
        len1 = din;
    else
        len1 = 7'b0 ;
end

//len第二位
always @(*) begin
    if(state_c == LEN && len_cnt == 1)
        len2 = din;
    else
        len2 = 7'b0 ;
end

//len1打拍得到len3
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        len3 <= 7'b0 ;
    else
        len3 <= len1;
end

//len的值
always @(*) begin
    len0 = {len3,len2};
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        len_fin <= 14'b0 ;
    else if(state_c == LEN)
        len_fin <= len0;
    else 
        len_fin <= len_fin ;
end

//状态机七个状态转移条件
assign IDLE_TYPE    = (head_flag2 && head_flag3) ;  
assign TYPE_CONTROL = (din == 8'ha0) ;
assign TYPE_LEN     = (din == 8'h66) ;
assign LEN_DATA     = (len_cnt == 1) ;
assign CONTROL_FCS  = (control_cnt == 63) ;
assign DATA_FCS     = (data_cnt == len_fin-1) ;
assign FCS_IDLE     = (fcs_cnt == 3) ;

//状态机第一段
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        state_c <= IDLE;
    else
        state_c <= state_n;
end

//状态机第二段
always @(*) begin
    case (state_c)
        IDLE : begin
            if(IDLE_TYPE)
                state_n = TYPE ;
            else
                state_n = state_c ;
        end
        TYPE : begin
            if(TYPE_CONTROL)
                state_n = CONTROL ;
            else if(TYPE_LEN)
                state_n = LEN ;
            else
                state_n = state_c ;
        end
        CONTROL : begin
            if(CONTROL_FCS)
                state_n = FCS ;
            else
                state_n = state_c ;
        end   
        LEN : begin
            if(LEN_DATA)
                state_n = DATA ;
            else
                state_n = state_c ;
        end   
        DATA : begin
            if(DATA_FCS)
                state_n = FCS ;
            else
                state_n = state_c ;
        end    
        FCS : begin
            if(FCS_IDLE)
                state_n = IDLE ;
            else
                state_n = state_c ;
        end  
        default: state_n = IDLE;   
    endcase
end

//状态机第三段
always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        dout <= 1'b0 ;
    else if(state_c == CONTROL || state_c == DATA || state_c == FCS)
        dout <= din ;
    else
        dout <= dout ;
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        dout_vld <= 1'b0 ;
    else if(state_c == CONTROL || state_c == DATA || state_c == FCS)
        dout_vld <= 1'b1 ;
    else
        dout_vld <= 1'b0 ;
end

endmodule

tb文件

timescale 1 ns/1 ns

module exam_tb();

    reg           clk       ; 
    reg           rst_n     ; 
    reg   [7:0]   din       ; 
    
    reg   [15:0]  len       ;

    wire  [7:0]   dout      ;
    wire          dout_vld  ;

    //参数定义
    parameter CYCLE    = 20;
    parameter RST_TIME = 3 ;

    //模块例化
exam u_exam(

    /*input           */.clk     (clk     ),
    /*input           */.rst_n   (rst_n   ),
    /*input   [7:0]   */.din     (din     ),
    /*output  [7:0]   */.dout    (dout    ),
    /*output          */.dout_vld(dout_vld)

);


        initial begin
            clk = 1;
            forever
            #(CYCLE/2)
            clk=~clk;
        end

        initial begin
        rst_n = 1;
        #2;
        rst_n = 0;
        #(CYCLE*RST_TIME);
        rst_n = 1;
    end

    initial begin
        #1;
        din = 0;
        #(15*CYCLE);
        repeat(5)begin 
            din = {$random};
            #(1*CYCLE);
        end 

        din = 8'h55;
        #(1*CYCLE);
        din = 8'hd5;//报文头
        #(1*CYCLE);
        din = 8'ha0;//报文类型 控制报
        #(1*CYCLE);
        repeat(80)begin         //报文数据 + FCS
            din = {$random};
            #(1*CYCLE);
        end 

        din = 8'h55;
        #(1*CYCLE);
        din = 8'hd5;//报文头
        #(1*CYCLE);
        din = 8'h66;//报文类型 数据报
        #(1*CYCLE);
        din = 8'h00;
        #(1*CYCLE);
        len = {$random};
        din = 8'hf0;//报文长度
        #(1*CYCLE);
        repeat(len)begin         //报文数据 
            din = {$random};
            #(1*CYCLE);
        end
        
        repeat(10)begin         //FCS 
            din = {$random};
            #(1*CYCLE);
        end
        
        $stop;

    end

endmodule

四、仿真

当复位后,检测到第一个报头,然后检测到控制报文类型后输出64B的data和4位的FCS,回到IDLE状态重新准备检测报头,检测到第二个报头后,再检测到数据报文类型后,检测到后面两位长度,然后开始输出此长度的数据,数据输出完后输出四位的FCS,然后再次回到IDLE状态。

控制报文和数据报文
在这里插入图片描述

控制报文开始
在这里插入图片描述

控制报文结束
在这里插入图片描述

数据报文开始在这里插入图片描述

数据报文结束在这里插入图片描述

检测到报头后没有找到两种报文返回IDLE状态
在这里插入图片描述

可改进的地方

得到len值的部分写的太过复杂,可以简化。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值