一、当FIFO存储的数据等于水线cfg_thd时,数据输出,此时的读使能为:
assign rd_en = rdusedw >= cfg_thd && empty == 0;
//无论何时,读的第一条件是empty == 0,即FIFO不为空
二、状态切换,即不仅存在启动水线,也同时存在停止水线
要求: 当FIFO中存储的数据大于等于启动水线时发送数据,即发送状态;当数据个数小于停止水线时停止发送,即空闲状态
cfg_thd_0 :停止水线
cfg_thd_1 :启动水线
分析 : 很显然,次FIFO存在两个明显的状态,即:发送状态和空闲状态。 空闲状态下,rdusedw >= cfg_thd_1跳到发送状态;发送状态下,rdusedw < cfg_thd_0 跳到空闲状态下。用flag来标识,flag == 1是发送状态,flag == 0 是空闲状态。
flag信号的设计
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag <= 0;
end
else if(flag == 0 && rdusedw >= cfg_thd_1)begin
flag <= 1;
end
else if(flag == 1 && rdusedw < cfg_thd_0)begin
flag <= 0;
end
end
rd_en 信号设计
assign rd_en = flag && empty == 0; //发送状态下,FIFO内有数据就读出数据
三、收到eop或者存储的数据大于等于250个时开始发送数据(即FIFO开始读取),读取完一个完整的包文才开始传输下一个。
分析: 相比 【二】 来说,多了一个eop的条件,如果直接使用eop来作为读信号,那么在下一个报文较短时,会出现读数据的混乱。而此处隐藏了发送状态与空闲状态,在空闲状态下,接收到eop或者FIFO存储数据大于等于250开始发送,即进入发送状态;在发送状态下,发送完一整个包文时进入空闲状态。
由于读写隔离原则,使用信息FIFO来标识何时进行读取。
信息FIFO的wr_en和wdata设计
assign wr_en = din_vld && din_eop; //判断是否有一个完整的包文
assign wdata = 1; //此情况不需要信息FIFO进行存储有效数据,故存储任意值均可
flag(空闲与发送指示)信号的设计
assign rd_en = empty==0 && flag;
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag <= 0;
end
else if(flag == 0 && (rdusedw >= 250 || msg_empty == 0))begin
flag <= 1;
end
else if(msg_rd)begin //msg_rd 为信息FIFO的读使能
flag <= 0;
end
end
assign msg_rd = rd_en && q[8]; // q[8]为dout_eop 即在发送完一个包文时清掉信息FIFO总线上的值
四、多选一进行读写的FIFO
这里也要进行分类,看是选择如何输入和输出,是有完整包文输出还是只要达到选择条件就输出。
(一) 不需要完整包文输出,只要有数据就根据要求输出。
FIFO00有数据,就发FIFO00;FIFO00没有数据而FIFO01有数据,就发FIFO01;FIFO00和FIFO01没数据,而FIFO02有效,就发FIFO02数据。
由于不需要完整包文和其他附加条件,所以不需要信息FIFO
wr_en 和wdata
assign wr_en0 = din_vld_a;
assign wdata_a = din_a;
assign wr_en1 = din_vld_b;
assign wdata_b = din_b;
assign wr_en2 = din_vld_c;
assign wdata_c = din_c;
//读使能
assign rd_0 = empty_a == 0;
assign rd_1 = empty_a && empty_b == 0;
assign rd_2 = empty_a && empty_b && empty_c == 0;
dout and chan
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout <= 0;
end
else if(rd_0)begin
dout <= q_0;
end
else if(rd_1)begin
dout <= q_1;
end
else if(rd_2)begin
dout <= q_2;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
chan <= 0;
end
else if(rd_0)begin
chan <= 0;
end
else if(rd_1)begin
chan <= 1;
end
else if(rd_2)begin
chan <= 2;
end
end
(二) 完整包文输出,根据要求选择FIFO输出。(不需要存储完整包文后再选择发送)
选中一个通道时就把这个包文输出完整,即输出到eop再进行下一次选择。
是否选中信号work_flag构造
//注意,选中与不选中的条件
//选中一个通道时,就一直读数据,直到读出一个完整包文结束该选中状态
//选中的状态:在空闲时,即没选中时,任意一FIFO内有数据就输出,进入选中状态
//未选中状态:在选中状态时,该通道输出EOP,结束选中状态进入未选中状态
assign work_flag_start = work_flag == 0 && (empty_0==0 || empty_1==0 || empty_2==0);
assign work_flag_stop = work_flag && dout_eop_tmp;
assign dout_eop_tmp = dout_0_tmp || dout_1_tmp || dout_2_tmp ;
/////////////////////////////////////////////////////////////////////////////////////////////
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
work_flag <= 0;
end
else if(work_flag_start)begin
work_flag <= 1;
end
else if(work_flag_stop)begin
work_flag <= 0;
end
end
//选择哪个FIFO输出
//FIFO_0有数据时选中FIFO_0,直到FIFO_0输出EOP
//FIFO_0无数据而FIFO_1有数据时,选择FIFO_1数据,直到FIFO_1输出EOP结束选中状态
//FIFO_0和FIFO_1无数据而FIFO_2有数据时,选择FIFO_2输出,直到FIFO_2输出EOP结束选中状态
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag_sel <= 0;
end
else if(work_flag_start)begin
if(empty_0==0)begin
flag_sel <= 0 ;
end
else if(empty_1)begin
flag_sel <= 1 ;
end
else if(empty_2)begin
flag_sel <= 2 ;
end
end
end
FIFO的读信号设计
assign rd_en_0 = work_flag && flag_sel == 0 && empty_0 == 0;
assign rd_en_1 = work_flag && flag_sel == 1 && empty_1 == 0;
assign rd_en_2 = work_flag && flag_sel == 2 && empty_2 == 0;
(三) 完整包文输出,需要收到完整的包文后再进行选择FIFO输出。
选中一个通道时就把这个包文输出完整,即输出到eop再进行下一次选择。
数据FIFO的写信号和写数据构造
assign wr_en_0 = din_a_vld ;
assign wdata_0 = {din_a_sop,din_a_eop,din_a};
assign wr_en_1 = din_b_vld ;
assign wdata_1 = {din_b_sop,din_b_eop,din_b};
assign wr_en_2 = din_c_vld ;
assign wdata_2 = {din_c_sop,din_c_eop,din_c};
信息FIFO构造(因为要在存储完一个完整包文时才选择输出,根据读写隔离原则,使用信息FIFO来标明读条件)
//输入一次完整的包文,即遇到din_a_eop/din_b_eop/din_c_eop 后将信息FIFO读使能拉高
assign msg_wr_en_0 = din_a_vld && din_a_eop ;
assign msg_wdata_0 = 1;
assign msg_wr_en_1 = din_b_vld && din_b_eop ;
assign msg_wdata_1 = 1;
assign msg_wr_en_2 = din_c_vld && din_c_eop ;
assign mag_wdata_2 = 1;
是否选中状态选择,与(二)中条件类似,但是不完全相同
//也是分为开始选择与结束选择
//开始选择条件为存储完完整包文时,FIFO0有数据则选择FIFO0;FIFO0无数据但FIFO1有数据则选择FIFO1;若只有FIFO2有数据,则选择FIFO2的数据输出
assign work_flag_start = work_flag == 0 && (msg_empty_0 == 0 || msg_empty_1 == 0 || msg_empty_2 == 0);
assign work_flag_stop = work_flag && dout_eop_tmp ;
assign dout_eop_tmp = q_a[8] && q_b[8] && q_c[8];
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
work_flag <= 0;
end
else if(work_flag_start)begin
work_flag <= 1;
end
else if(work_flag_stop)begin
work_flag <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag_sel <= 0;
end
else if(flag_work_start)begin
if(msg_empty_0==0)begin
flag_sel <= 0;
end
else if(msg_empty_1)begin
flag_sel <= 1;
end
else if(msg_empty_2)begin
flag_sel <= 2;
end
end
end
数据FIFO读使能以及信息FIFO读使能
//信息FIFO
//在发送完一个完整的包文时将总线上的数据清掉
assign msg_rd_en_0 = dout_eop_a;
assign msg_rd_en_1 = dout_eop_b;
assign msg_rd_en_2 = dout_eop_c;
assign dout_eop_a = q_a[8] && rd_en_a;
assign dout_eop_b = q_b[8] && rd_en_b;
assign dout_eop_c = q_c[8] && rd_en_c;
//数据FIFO的读使能
assign rd_en_0 = work_flag && flag_sel == 0 && empty_a == 0;
assign rd_en_1 = work_flag && flag_sel == 1 && empty_b == 0;
assign rd_en_2 = work_flag && flag_sel == 2 && empty_c == 0;
五、多通道数据FIFO选择输出,各个通道数据位不同,而输出的数据位相同
示例:
(一)
- A通道是输入8bit数据 ,FIFO要求输出16bit
B通道是输入16bit数据,FIFO要求输出16bit
C通道是输入32bit数据,FIFO要求输出16bit
不需要存入完整包文,只要有数据就读 - 分析:因为输入都不一致,分别为8、16、32bit,而输出为16bit,那么显然,A通道进来的数据需要在计数器的作用下将两次数据合并成一个16bit的数据进行输出;B通道进来的数据可以直接按选择输出;C通道进来的数据是32bit,那么需要在计数器的处理下,使用两个时钟将32bit转换成16bit输出。
写侧数据处理
A通道数据处理
//计数两个A通道数据后写入FIFO,而当数据不是偶数时,即计数到din_a_eop或者2就清0计数器
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 begin
cnt0 <= cnt0 + 1'b1;
end
end
end
assign add_cnt0 = din_a_vld;
assign end_cnt0 = add_cnt0 && (cnt0 == 2-1 || din_a_eop);
//关于A通道的数据,SOP,EOP,MTY(无效数据数),VLD等信号的设计
//要分开设计,因为各个信号的条件不同
//wdata_0_data
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_0_data <= 0;
end
else if(add_cnt0)begin
wdata_0_data[15 - 8*cnt0 -:8] <= din_a;
end
end
//wdata_0_sop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_0_sop <= 0;
end
else if(add_cnt0 && cnt0 == 1-1)begin
wdata_0_sop <= din_a_sop;
end
end
//wdata_0_eop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_0_eop <= 0;
end
else if(end_cnt0)begin
wdata_0_eop <= din_a_eop;
end
end
//wdata_0_mty
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_0_mty <= 0;
end
else if(din_a_vld && din_a_eop)begin
wdata_0_mty <= 1 - cnt;
end
else begin
wdata_0_mty <= 0;
end
end
//wdata_0_en
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_0_en <= 0;
end
else begin
wdata_0_en <= end_cnt0;
end
end
assign wdata_0 = {wdata_0_sop,wdata_0_eop,wdata_0_mty,wdata_0_data};//SOP,EOP,MTY,DIN[15:0]
B通道:
assign wdata_1 = {wdata_1_sop,wdata_1_eop,wdata_1_mty,din_b};
assign wr_en_1 = din_b_vld;
C通道:
//C通道是32bit,在此处直接存入FIFO,之后在读侧进行处理
//此处的数据FIFO宽度为32bit
assign wdata_2 = {wdata_2_sop,wdata_2_eop,wdata_2_mty,din_c};
assign wr_en_2 = din_c_vld;
通道选择
//work_flag_start :表示选中通道信号,在FIFO_A、FIFO_B、FIFO_C中有数据时选中信号拉高,开始输出数据
//work_flag_stop :当输出该选中的FIFO包文的EOP时,结束选择状态
assign work_flag_start = work_flag==0 && (empty_a == 0 || empty_b == 0 || empty_c == 0);
assign work_flag_stop = work_flag && dout_eop_tmp;
assign dout_eop_tmp = rd_a_eop || rd_b_eop || rd_c_eop;
//选中状态时,FIFO处于工作状态,即发送状态
//结束选中状态时,FIFO处于空闲状态
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
work_flag <= 0;
end
else if(work_flag_start)begin
work_flag <= 1;
end
else if(work_flag_stop)begin
work_flag <= 0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag_sel <= 0;
end
else if(flag_work_start)begin
if(empty_a==0)begin
flag_sel <= 0;
end
else if(empty_b==0)begin
flag_sel <= 1;
end
else if(empty_c==0)begin
flag_sel <= 2;
end
end
end
读侧数据处理
assign rd_en_a = flag_work && flag_sel == 0 && empty_a == 0;
assign rd_en_b = flag_work && flag_sel == 1 && empty_b == 0;
assign rd_en_c = flag_work && flag_sel == 2 && empty_c == 0;
assign rd_sop_a = rd_en_a && q_a[18];
assign rd_sop_b = rd_en_b && q_b[18];
assign rd_sop_c = add_cnt1 && cnt1 == 1-1 && q_c[35];
assign rd_a_eop = rd_en_a && q_a[17];
assign rd_b_eop = rd_en_b && q_b[17];
assign rd_c_eop = rd_en_c && q_c[34];
assign rd_mty_a = rd_en_a && q_a[16];
assign rd_mty_b = rd_en_b && q_b[16];
assign rd_mty_c = rd_en_c && q_c[32];
//读侧计数器
//C通道要输出两次,FIFO才读一次
//此处使用变量法的原因是由于并不一定输出的32bit数据都有效
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 begin
cnt1 <= cnt1 + 1'b1;
end
end
end
assign add_cnt1 = flag_work && flag_sel == 2 && empty_c == 0;
assign end_cnt1 = add_cnt1 && cnt1 == x - 1;
always @(*)begin
if(q_c[33])begin
x = 2 - q_c[32];
end
else begin
x = 2;
end
end
//选择通道输出数据data_d
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
data_d <= 0;
end
else if(rd_en_a)begin
data_d <= q_a[15:0];
end
else if(rd_en_b)begin
data_d <= q_b[15:0];
end
else if(rd_en_c)begin
data_d <= q_c[31 - 16*cnt1 -:16];
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 <= rd_sop_a || rd_sop_b || rd_sop_c ;
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 <= rd_eop_a || rd_eop_b || rd_eop_c ;
end
end
//dout_mty
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_mty <= 0;
end
else begin
dout_mty <= rd_mty_a || rd_mty_b || rd_mty_c ;
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 <= rd_en_a || rd_en_b || add_cnt1 ;
end
end
(二)
- 当FIFO_A中有完整的包文时,输出FIFO_A中的数据;
- 当FIFO_A中没有数据或者没有完整的包文,而FIFO_B中有完整的包文数据时,选择FIFO_B输出;
- FIFO_A、FIFO_B中没有完整包文而FIFO_C中有完整包文,选择FIFO_C输出。
- A通道FIFO为8bit
- B通道FIFO为16bit
- C通道FIFO为32bit
- 输出的位数为16bit
分析:8bit通道需要在计数器作用下计数两次输出一个16bit的数据;而B通道输出刚好为16bit,可以直接输出;C通道为32bit,在计数器作用下分两次输出16bit.
A通道数据写侧处理
//使用计数器控制,输入两次写一次
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
cnt0 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt0 <= 0;
end
else begin
cnt0 <= cnt0 + 1'b1;
end
end
end
assign add_cnt0 = din_a_vld ;
assign end_cnt0 = add_cnt0 && (cnt0 == 2-1 || din_a_eop);
//wdata_a
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_a <= 0;
end
else if(add_cnt0)begin
wdata_a[15 -8*cnt0 -:8] <= din_a;
end
end
//wdata_a_sop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_a_sop <= 0;
end
else if(add_cnt0 && cnt0 == 1-1)begin
wdata_a_sop <= din_a_sop;
end
end
//wdata_a_eop
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_a_eop <= 0;
end
else if(end_cnt0)begin
wdata_a_eop <= din_a_eop;
end
end
//wdata_a_mty
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wdata_a_mty <= 0;
end
else if(din_a_vld && din_a_eop)begin
wdata_a_mty <= 1 - cnt0;
end
else begin
wdata_a_mty <= 0;
end
end
//计数两次写一次
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
wr_en_a <= 0;
end
else begin
wr_en_a <= end_cnt0;
end
end
//存入FIFO的数据
assign wdata_a_din = {wdata_a_sop,wdata_a_eop,wdata_a_mty,wdata_a};
//信息FIFO
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
msg_wr_en_a <= 0;
end
else begin
msg_wr_en_a <= din_a_vld && din_a_eop;
end
end
//信息FIFO的数据可以任意存储,因为只是起到一个标识作用
assign msg_wdata_a = 1;
//由于是根据时序进行数据拼写,为了保证时序的准确性,数据FIFO和信息FIFO的写使能都采用时序
B通道写侧数据处理
//数据FIFO
assign wr_en_b = din_b_vld;
assign wdata_b_din = {din_b_sop,din_b_eop,din_b_mty,din_b};
//信息FIFO
assign msg_wr_en_b = din_b_vld && din_b_eop;
assign msg_wdata_b = 1;
C通道写侧数据处理
//数据FIFO
assign wr_en_c = din_c_vld;
assign wdata_c_din = {din_c_sop,din_b_eop,din_c_mty,din_c};
//信息FIFO
assign msg_wr_en_c = din_c_vld && din_c_eop;
assign msg_wdata_c = 1;
工作状态和选择信号的构造
assign work_flag_start = work_flag == 0 && (msg_empty_a == 0 || msg_empty_b == 0 || msg_empty_c == 0);
assign work_flag_stop = work_flag && dout_eop_tmp ;
assign dout_eop_tmp = dout_eop_tmp_a || dout_eop_tmp_b || dout_eop_tmp_c ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
work_flag <= 0;
end
else if(work_flag_start)begin
work_flag <= 1'b1;
end
else if(work_flag_stop)begin
work_flag <= 1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
flag_sel <= 0;
end
else if(work_flag_start)begin
if(msg_empty_a == 0)begin
flag_sel <= 0;
end
else if(msg_empty_b == 0)begin
flag_sel <= 1;
end
else if(msg_empty_c == 0)begin
flag_sel <= 2;
end
end
end
读侧各个信号设计
//各个信息FIFO的读使能
assign msg_rd_en_a = dout_eop_tmp_a && rd_en_a;
assign msg_rd_en_b = dout_eop_tmp_b && rd_en_b;
assign msg_rd_en_c = dout_eop_tmp_c && rd_en_c;
assign dout_eop_tmp_a = q_a[17];
assign dout_eop_tmp_b = q_b[17];
assign dout_eop_tmp_c = q_c[33];
assign dout_sop_tmp_a = q_a[18];
assign dout_sop_tmp_b = q_b[18];
assign dout_sop_tmp_c = add_cnt1 && cnt1 == 1-1 && q_c[34];
assign dout_mty_tmp_a = q_a[16];
assign dout_mty_tmp_b = q_b[16];
assign dout_mty_tmp_c = q_c[32];
//数据FIFO的读使能
assign rd_en_a = flag_work && flag_sel == 0 && empty_a == 0;
assign rd_en_b = flag_work && flag_sel == 1 && empty_b == 0;
assign rd_en_c = end_cnt1;
//C通道将32bit数据分两次输出
always @(posedge clk or negedge rst_n)begin
if(rst_n)begin
cnt1 <= 0;
end
else if(add_cnt1)begin
if(end_cnt1)begin
cnt1 <= 0;
end
else begin
cnt1 <= cnt1 + 1'b1;
end
end
end
assign add_cnt1 = flag_work && flag_sel == 2 && empty_c == 0;
assign end_cnt1 = add_cnt1 && cnt1 == x - 1;
always @(*)bgein
if(q_c[33])begin
x = 2 - dout_mty_tmp_c;
end
else begin
x = 2;
end
end
// dout
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
data_d <= 0;
end
else if(rd_en_a)begin
data_d <= q_a[15:0];
end
else if(rd_en_b)begin
data_d <= q_b[15:0];
end
else if(add_cnt1)begin
data_d <= q_c[31 -16*cnt1 -:16];
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 <= dout_sop_tmp_a || dout_sop_tmp_b || dout_sop_tmp_c;
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 <= dout_eop_tmp;
end
end
//dout_mty
always @(posedge clk or negedge rst_n)begin
if(rst_n==0)begin
dout_mty <= 0;
end
else begin
dout_mty <= dout_mty_tmp_a || dout_mty_tmp_b || dout_mty_tmp_c;
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 <= rd_en_a || rd_en_b || add_cnt1;
end
end