||
最近在尝试在verilog(2018.2)中将ceo文件经过BRAM进行读出 ,随后进行一系列的操作。
首先是选择合适的RAM,以下是BRAM和DRAM的区别(来源于网络)
由于本文的CEO文件较大,故选择BRAM。
在IPCatalog中选择BRAM,如上图,随后进行IP核的设置
相关IP设置,仅作参考,点击选中LOAD INITFILE即可添加我们我们想要的COE文件,在CORE栏添加。
注:COE文件的头部格式应该为
其中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的读带宽,就可以完美解决这一问题。
上图可做参考。
成功实现文件中的数据按照1bit位宽输出后,就可以开始对帧头的位置进行检测,采用的方法是移位寄存器检测的方法
以上为帧头检测的程序,仅供参考。
检测到帧头后,接着可以将剩余数据输出,随后连同帧头和后续数据一起输出一帧完整的数据。在这一块笔者一度卡了好几天。一开始我的想法是定义一个寄存器,检测到帧头后就将剩余数据移位存入寄存器,随后利用位拼接将帧头和剩余数据拼接起来再输出。但是当数据量过大时,比如剩余字节有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
以上程序仅供参考。还有一些小问题
在一开始,对与DOUT2的输出,我只写了红色框内的部分,但是最终输出的数据有错误可以看到,d5795793这一串数据保持了两个时钟周期,因此导致了后续数据出现错误。
如上图,显然DOUT2是有错误的,会导致最终输出的结果出现错误,再添加了蓝色框内的部分后,结果就输出正确了。这里给出正确的波形图。可以看到检测后的数据与写入FIFO中的数据一致,一帧完整的数据成功存入FIFO。
以上内容仅供参考,实际只简单实现了一帧数据的帧头检测与发送,若要实现连续的帧头检测与帧发送,该程序还有不小的漏洞,有许多需要修改的地方。在接下来的改进方向中,我准备使用状态机实现帧头检测与后续数据的发送,并且可以实现连续的多帧检测。笔者还处于学习阶段,能力有限,可能程序会有许多错误与不足,望指正。
Archiver|手机版|科学网 ( 京ICP备07017567号-12 )
GMT+8, 2024-9-23 14:20
Powered by ScienceNet.cn
Copyright © 2007- 中国科学报社