JLFu的个人博客分享 http://blog.sciencenet.cn/u/JLFu

博文

利用Verilog将CEO文件中的帧头识别出来,并发送一帧完整的数据

已有 3430 次阅读 2021-10-23 18:13 |个人分类:Verilog|系统分类:科研笔记

最近在尝试在verilog(2018.2)中将ceo文件经过BRAM进行读出 ,随后进行一系列的操作。

首先是选择合适的RAM,以下是BRAM和DRAM的区别(来源于网络)

Dram和bram区别:

    1、bram 的输出需要时钟,dram在给出地址后既可输出数据。

    2、bram有较大的存储空间,是fpga定制的ram资源;而dram是逻辑单元拼出来的,浪费LUT资源

    3、dram使用更灵活方便些

由于本文的CEO文件较大,故选择BRAM。

image.png

在IPCatalog中选择BRAM,如上图,随后进行IP核的设置

image.png

image.png

image.png

相关IP设置,仅作参考,点击选中LOAD INITFILE即可添加我们我们想要的COE文件,在CORE栏添加。

注:COE文件的头部格式应该为

6062a67fdefccf27bd7de5365dafda9.png

其中RADIX表示后续数据的进制,可以为2,8,10,16。后续的数据应与选择的进制相符,如选择8进制则不能出现大于8的数字,如9。。

在文件添加成功后,需要设置BRAM的写入带宽与读出带宽。最开始文件中的数据位16进制,而我要对帧头进行移位检测,因此设置了写入带宽位4bit,读出带宽为1bit。但这么做出现了问题。比如一串16进制数1ACFFC1D,转成二进制即为,0001_1010_1011_1111_1111_1011_0001_1101。这样写入时没有问题,但在按照1bit位宽,读出这些数据时,读出的数据变成了1000_0101_1101_1111_1111_1101_1000_1011。显然在按照一位的位宽读出后,每一个16进制数的高低位都反过来了。这显然是错误的。但是如果RAM的写入和读出带宽都为4比特时,就不会出现这样的问题。这一块具体的原理还没搞明白。

如果最终想输出串行的1位位宽的数据,可以在RAM后加上一个FIFO,FIFO设置为4bit写位宽,1bit的读带宽,就可以完美解决这一问题。

image.png

上图可做参考。

成功实现文件中的数据按照1bit位宽输出后,就可以开始对帧头的位置进行检测,采用的方法是移位寄存器检测的方法

image.png

以上为帧头检测的程序,仅供参考。

检测到帧头后,接着可以将剩余数据输出,随后连同帧头和后续数据一起输出一帧完整的数据。在这一块笔者一度卡了好几天。一开始我的想法是定义一个寄存器,检测到帧头后就将剩余数据移位存入寄存器,随后利用位拼接将帧头和剩余数据拼接起来再输出。但是当数据量过大时,比如剩余字节有1020字节,这种情况就要定义一个位宽为8160位的寄存器,这样显然十分麻烦且占用资源。随后笔者又想到定义一个wire型的变量,将剩余数据直接和帧头拼接到一起输出。但wire型变量无法再always块中进行非阻塞赋值。这个方法显然也不行。在询问了师兄后,想到了可以再利用FIFO,将帧头数据先存入FIFO,随后再将剩余数据存入FIFO,这个时候就可以从FIFO中读取出一帧完整的数据。

module xuliejiance (

    input             clk         ,

    input             rst_n       ,

    input             din         ,

    output reg [31:0] dout_valid ,//有效数据

    output reg        wr_en_xulieout

);

reg [31:0] dout1;

reg zt_valid;

reg [4:0]cnt_dout2;//移位32次,计数32次

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        dout1<=32'b0;

    end

    else begin

        dout1<={dout1[30:0],din};//将输入数据移位赋给dout1

    end

end

always@(posedge clk or negedge rst_n)begin

    if(!rst_n)begin

        dout_valid<=32'h0;

    end

    else begin

        if(dout1==32'h1A_CF_FC_1D)begin

            dout_valid<=dout1;//检测到帧头,将帧头数据输出

           

        end

        else begin

            dout_valid<=dout_valid;            

        end

    end

end

reg [31:0] dout2;//后续数据,32bit发一次

always@(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

         cnt_dout2<=5'b0;

//         dout2<=32'b0;

       end

   else begin

     if(cnt_dout2==5'h1f)begin//移位32次后将dout2输出给dout_valid

          dout_valid<=dout2;//?????

          cnt_dout2<=5'b0;

//          dout_valid<=dout2;

        end

    else if(zt_valid==1'b1)begin//zt_valid在帧头检测到后全程拉高

           cnt_dout2<=cnt_dout2+1'b1;//移位计数共计数32次

//           dout2<={dout2[30:0],din};

    end

        else begin

            cnt_dout2<=cnt_dout2;//计数器保持

        end

   end

end

always@(posedge clk or negedge rst_n)begin

       if(!rst_n)begin

          dout2<=32'b0;//初始值赋0

       end

        else if(zt_valid==1'b1)begin

            dout2<={dout2[30:0],din};//移位存储

        end

       else begin

           dout2<=dout2;

       end

end

reg [7:0] zhen_cnt;//对帧头进行计数

reg [13:0] cnt;//对整个帧的数据进行计数

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        zt_valid<=1'b0;

    end

    else if(dout1==32'h1A_CF_FC_1D)begin

        zt_valid<=1'b1;

    end

    else begin

        zt_valid<=zt_valid;

    end

end

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        zhen_cnt<=8'b0;

    end

    else if(dout1==32'h1A_CF_FC_1D)begin

        zhen_cnt<=zhen_cnt+1'b1;

    end

    else if(zhen_cnt==8'd64)begin

        zhen_cnt<=8'b0;

    end

    else begin

        zhen_cnt<=zhen_cnt;

    end

end

always @(posedge clk or negedge rst_n) begin

    if(!rst_n)begin

        cnt<=14'b0;

    end

    else begin

     if(cnt==14'h1fdf)begin

        cnt<=14'b0;

        zt_valid<=1'b0;

        end

     else if(zt_valid==1'b1)begin

        cnt<=cnt+1'b1;

    end

    else begin

        cnt<=cnt;

    end

    end

end

//产生FIFO写使能端信号

always@(posedge clk or negedge rst_n)begin

     if(!rst_n)begin

         wr_en_xulieout<=1'b0;

     end

     else if(cnt==14'h1fdf||cnt_dout2==5'h1f||dout1==32'h1A_CF_FC_1D)begin

         wr_en_xulieout<=1'b1;

     end

     else begin

         wr_en_xulieout<=1'b0;

     end

end

endmodule

以上程序仅供参考。还有一些小问题

image.png


在一开始,对与DOUT2的输出,我只写了红色框内的部分,但是最终输出的数据有错误可以看到,d5795793这一串数据保持了两个时钟周期,因此导致了后续数据出现错误。

image.png

如上图,显然DOUT2是有错误的,会导致最终输出的结果出现错误,再添加了蓝色框内的部分后,结果就输出正确了。这里给出正确的波形图。可以看到检测后的数据与写入FIFO中的数据一致,一帧完整的数据成功存入FIFO。

image.png

image.png

以上内容仅供参考,实际只简单实现了一帧数据的帧头检测与发送,若要实现连续的帧头检测与帧发送,该程序还有不小的漏洞,有许多需要修改的地方。在接下来的改进方向中,我准备使用状态机实现帧头检测与后续数据的发送,并且可以实现连续的多帧检测。笔者还处于学习阶段,能力有限,可能程序会有许多错误与不足,望指正。



https://blog.sciencenet.cn/blog-3491675-1309163.html

上一篇:verilog及自带代码编辑器的常用快捷键(1)
收藏 IP: 124.115.222.*| 热度|

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-3-29 02:08

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部