一、模块没收到一个包文时,先发送一个开始包文,再发送数据包文,最后发送结束包文。
输入的包文是32bit,而输出的包文格式要求为8bit
开始包文格式如下:
8’h55 | 8’'h55 | 8’h55 | 8’hd5 | 8’hd5 | pack_id |
---|
数据包文格式与上游输入的包文一致
结束包文格式如下:
8’h55 | 8’'h55 | 8’h55 | 8’hfd | 8’hfd | pack_id |
---|
注:pack_id是指进来包文的id信号,第一个包文id为0,后面依次累加
//部分信号列表
input [31:0] din ;
input din_sop ;
input din_eop ;
input [1:0] din_mty ;
output [7:0] reg dout ;
output reg dout_sop ;
output reg dout_eop ;
output reg dout_vld ;
分析:
显然,输入与输出位数不匹配,那么应该怎么办?很简单,采用计数器分四次将一个32bit数据传输完成。
问题1:如何划分开始、数据和结束包文?
不妨设计两个计数器,一个计数器记开始与结束,因为都是6*8bit,这个计数器记完会跳入数据计数器,计数完毕后则又会跳回结束计数器。
//写侧数据FIFO和信息FIFO的使能及数据
assign wr_en = din_vld ;
assign wdata = din;
assign msg_wr_en = din_vld && din_eop;
assign msg_wdata = 1;
//因为开始跟结束包文都是6bit,所以需要计数器计数
//需要避过发送数据阶段,使用cnt0表示开始结束,使用cnt1表示三个阶段
// cnt1 == 1-1 :开始阶段
// cnt1 == 2-1 :发送数据阶段
// cnt1 == 3-1 :结束阶段
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt0 <= 0;
end
else if(add_cnt0)begin
if(end_cnt0)begin
cnt0 <= 0;
end
else
cnt0 <= cnt0 + 1'b1;
end
end
assign add_cnt0 = msg_empty == 0 && cnt1 != 2-1;
assign end_cnt0 = add_cnt0 && cnt0 == 6-1;
//计数条件是开始包文发送完成,数据包文发送完成,结束包文发送完成
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 0;
end
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = end_cnt0 && dout_eop_tmp;
assign end_cnt1 = add_cnt1 && cnt1 == 3-1;
assign dout_eop_tmp = rd_en && q[34];
//将32bit做并串转换。转换成8Bit
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)begin
cnt2 <= 0;
end
else
cnt2 <= cnt2 + 1'b1;
end
end
assign add_cnt2 = msg_empty == 0 && cnt1 == 2-1;
assign end_cnt2 = add_cnt2 && cnt2 == X - 1; //一般计数是4个,但是要看din_mty
always @(*)begin
if(q[34])begin
X = 4 - q[33:32];
end
else begin
X = 4;
end
end
//数据FIFO的读使能
//输出4个8bit数据才读一次
assign rd_en = end_cnt2;
//信息FIFO的读使能
assign msg_rd_en = end_cnt1; //msg_q 一定要在最后一刻清0
//pack_id代码
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
pack_id <= 0;
end
else if(end_cnt1)begin
pack_id <= pack_id + 1'b1;
end
end
//dout
//先将数据组合起来方便传输
assign data_sel = (cnt1 == 1-1) ? 16'hd5d5:16'hfdfdl;
assign ctrl_data = {8'h55,8'h55'8'h55,data_sel,pack_id};
always @(posedge clk or negedge rst_n)begin
if(rst_n == 0)begin
dout <= 0;
end
else if(add_cnt0)begin
dout <= ctrl_data[47 - 8*cnt0 -:8];
end
else begin
dout <= q[32 -8*cnt2 -:8];
end
end
//dout_sop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_sop <= 0;
end
else begin
dout_sop <= (add_cnt0 && cnt0 == 1-1) || (add_cnt1 && cnt1 == 2-1 && q[35]) ;
end
end
//dout_eop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_eop <= 0;
end
else begin
dout_eop <= end_cnt0 || dout_eop_tmp ;
end
end
//dout_vld
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_vld <= 0;
end
else begin
dout_vld <= add_cnt0 || add_cnt2;
end
end
二、包文分段
要求:模块将输入的包文切成1个或者多个小于等于256字节的数据包文发送给下游模块。假设包文长度为len当len <=256字节时,无须切割,数据包文长度为len:当len > 256,且<=256字节时,切割成2个数据包文,第一个包文长度为256,第二个包文长度为len-256,依次类推。
输入8bit,输出8bit
开始包文格式如下:
8’h55 | 8’'h55 | 8’h55 | 8’hd5 | 8’hd5 | pack_id |
---|
数据包文格式与上游输入的包文一致
结束包文格式如下:
8’h55 | 8’'h55 | 8’h55 | 8’hfd | 8’hfd | pack_id |
---|
注:pack_id是指进来包文的id信号,第一个包文id为0,后面依次累加
关于区分开始包文,数据包文和结束包文的方法与【一】相同,此处就不再重复。
分析:
关于切割部分,使用计数器将其切割
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt2 <= 0;
end
else if(add_cnt2)begin
if(end_cnt2)begin
cnt2 <= 0;
end
else begin
cnt2 <= cnt2 + 1;
end
end
end
//256个一段,除非是最后一段碰到DOUT_EOP
assign add_cnt2 = msg_empty == 0 && cnt1 == 2-1;
assign end_cnt2 = add_cnt2 && (cnt2 == 256 - 1 || last_data);
assign last_data = cnt1 == rd_en && q[8];
//dout_sop
//注意此处的sop,在切割包文中也是有的
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_sop <= 0;
end
else begin
dout_sop <= (add_cnt0 && cnt0 == 1-1)||(add_cnt2 && cnt2 == 1-1);
end
end
//dout_eop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_eop <= 0;
end
else begin
dout_eop <= end_cnt0 ||end_cnt2;
end
end
//dout_vld
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_vld <= 0;
end
else begin
dout_vld <= add_cnt0 || add_cnt2;
end
end
三、模拟以太网切割包文
此处切割后并不知道这是第几个包文,只知道分成了1500和46以内,功能较为简单,后面会说如何设计知道这是被分成了几个包文。
以太网包模块的功能是对输入的包文进行切割,使输出的每个包文都控制在46~1500字节范围内。
如果输入包文长度len < 46字节,则通过在末尾补0的方式,输出长度为46字节的包文;
如果输入包文长度len > (1500n)且len<=(1500n + 46)[n>=1]时,切分成n-1个1500长度的包文,1个(len-1500*(n-1)-46)长度包文和1个46字节的包文。
如果输入包文长度len <= (1500*(n+1))且len>(1500n+46)[n>=1]时,切分成n个1500长度的包文,1个(len-1500n)长度包文
//数据FIFO的写数据和写使能
assign wr_en = din_vld;
assign wdata = {din_sop,din_eop,din};
//在写侧进行包文的计数
//写侧进行切割的话,读侧就可以直接读
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else
cnt <= cnt + 1'b1;
end
end
assign add_cnt = din_vld ;
assign end_cnt = add_cnt && (din_eop || add_cnt == 1500-1);
//信息FIFO的写使能和写数据
//将切割后的长度存入信息FIFO
assign msg_wr_en = end_cnt;
assign msg_wdata = cnt + 1;
//读出的包文长度
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 0;
end
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = msg_empty == 0;
assign end_cnt1 = add_cnt1 && cnt1 == x-1;
always @(*)begin
if(msg_q < 46)begin
x = 46;
end
else
x = msg_q;
end
四、设计指示将以太网按照要求切割了多少个包文
//使用两个计数器,一个计1500,一个计46,使用flag信号来告诉计数器什么时候计1500,什么时候计46
//为什么要计46?
//因为若是只计数1500,那么大于1500而小于1546的部分便无法分辨,从而有可能导致切割出错
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else
cnt <= cnt + 1'b1;
end
end
assign add_cnt = din_vld ;
assign end_cnt = add_cnt && (din_eop || add_cnt == 1500-1);
//
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 0;
end
else
cnt1 <= cnt1 + 1'b1;
end
end
assign add_cnt1 = din_vld && flag == 1;
assign end_cnt1 = add_cnt1 && (din_eop || add_cnt1 == 46-1);
//flag :当cnt计数了1500时,拉高,cnt1开始计数,计数完46时将flag拉低
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag <= 0;
end
else if(end_cnt && din_eop == 0)begin
flag <= 1;
end
else if(end_cnt1)begin
flag <= 0;
end
end
//数据FIFO存储的数据和写使能与上面相同,但是此处的信息FIFO需要注意
//信息FIFO的写使能和写数据
always @(*)begin
if(din_vld && din_eop)begin
if(flag == 1)
msg_wdata = 1500 + cnt1 + 1;
else
msg_wdata = cnt + 1;
end
else
msg_wdata = 1500;
end
//遇到EOP写或者end_cnt1记完
//46记完是指包文长度大于1546了,所以写1500到信息FIFO
assign msg_wr_en = end_cnt1 || (din_vld && din_eop);
//读侧的计数器
//计数读侧输出的数据字节
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt2 <= 0;
end
else if(add_cnt1)begin
if(end_cnt2)begin
cnt2 <= 0;
end
else
cnt2 <= cnt2 + 1'b1;
end
end
assign add_cnt2 = msg_empty == 0;
assign end_cnt2 = add_cnt2 && cnt2 == x+z-1;
//计数分成了多少个包文
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt3 <= 0;
end
else if(add_cnt3)begin
if(end_cnt3)begin
cnt3 <= 0;
end
else
cnt3 <= cnt3 + 1'b1;
end
end
assign add_cnt3 = end_cnt2;
assign end_cnt3 = add_cnt3 && add_cnt3 == y-1;
//x 是 发送的实际数据,z是补0的个数
//当大于1500,发两个包文,一个是46,一个是长度-46
always @(*)begin
if(msg_q < 46)begin
x = msg_q ;
y = 1 ;
z = 46 - msg_q;
end
else if(msg_q < 1501)begin
x = msg_q ;
y = 1;
z = 0;
end
else begin //msg_q >1500
if(cnt3 == 0)begin
x = msg_q - 46; //长度大于1500,一个包文发46,一个发长度-46
y = 2 ;
z = 0 ;
end
else begin
x = 46 ;
y = 2;
z = 0;
end
end
end
//数据FIFO的读使能
//*****注意不必输出补的0位字节********
assign zero_n = add_cnt2 && cnt2 < x ;
assign rd_en = add_cnt2 && zero_n;
//信息FIFO的读使能
assign msg_rd_en = end_cnt3 ;
//dout
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout <= 0;
end
else begin
dout <= q[7:0];
end
end
//dout_sop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_sop <= 0;
end
else begin
dout_sop <= add_cnt2 && cnt2 == 1-1;
end
end
//dout_eop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_eop <= 0;
end
else begin
dout_eop <= end_cnt2;
end
end
//dout_vld
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_vld <= 0;
end
else begin
dout_vld <= add_cnt2;
end
end