FPGA 之 FIFO 求和算法

13 篇文章 11 订阅
        要实现 FIFO  求和, FIFO IP 核必不可少,需要用它用来做求和数据缓存。FIFO 是存储器的一种,满足先进先出原则,前面对它已经有了详细介绍,大家可自行翻阅我前面的博客。
        要完成 3 行数据的 FIFO   求和,需要调用 2 FIFO IP 核,当数据开始输入时,将数据的第 0 行数据存储到 fifo1 中,将第 1 行数据存储到 fifo2 中,当数据的第 2 行的第 0 个数据输入的同时,读取写入 fifo1 中的的第 0 个数据和写入 fifo2 中的第 0 个数据,将三个数据求和,求和结果实时输出,在完成求和的同时,将读取的 fifo2 中的第 0 个数据写入 fifo1 中,fifo1 读出的数据弃之不用,将输入的第 2 行的数据写入 fifo2 中,当第 2 行的最后一个数据输入,完成前三行的最后一个求和运算后,第 0 行的数据已读取完成,第 1 行的数据重新写入 fifo1 ,第 2 行的数据写入 fifo2 ,当第 3 行数据开始传入时,开始进行第 1 行、第 2 行和第 3 行的数据求和运算,如此循环,直到最后一个数据输入,完成求和运算。流程示意图具体见图 1 。
图 1 求和运算流程图
        FIFO 求和实验工程的整体框架如图 2 所示

图 2 实验工程整体框架

        系统上电后,使用 PC 机通过串口助手发送待求和数据给 FPGAFPGA 通过串口接收模块接收待求和数据,数据拼接完成后传入数据求和模块,经过求和运算后的数据结果通过串口数据发送模块回传给 PC 机,使用串口助手查看求和结果。 

        下面进行求和模块的编写说明:

图 3  FIFO 求和运算框架

        由模块框图 3 可知,数据求和模块包含 4 路输入、 2 路输出,共 6 路信号。输入输出信号简介可见如下表格:
信号位宽( bit )输入输出功能
sys_clk1input工作时钟,一般50MHz
sys_rst_n1input复位信号
pi_flag1input输入数据标志信号
pi_data8input输入待求和数据
po_flag1output输出数据标志信号
po_sum8output输出求和后数据

        下面是整体波形图配置:

图 4 数据求和模块波形图

        第一部分:行计数器 cnt_row 、列计数器 cnt_col 信号波形的设计与实现
        本实验是要实现 3 行数据的求和,那么需要对参与求和运算的每行数据个数进行计数,同样也需要对参与求和运算的各行进行计数,所以我们需要声明两个计数器行计数器cnt_row、列计数器 cnt_col
        变量 cnt_row 是行计数器,计数每行数据的个数,我们可以以输入数据标志信号 pi_flag 为约束条件进行计数。 cnt_row 计数器初值为 0 pi_flag 信号每拉高一次,计数器加 1,当 cnt_row 计数器计到最大值 ( 一行数据个数减 1 ,本实验一行数据 50 个,计数器计数最大值为 50-1=49) ,行计数器归 0 ,开始下一行计数;
        cnt_col 是列计数器,对输入数据进行列计数 ( 计数行个数 ) ,计数器初值为 0 ,行计数器计数到最大值且 pi_flag 信号有效时,列计数器加 1 ,列计数器计到最大值 ( 行个数减 1 ,本实验数据共有 50 行,计数器计数最大值为 50-1=49) ,列计数器归 0 。两计数器计数器信号波形如下。

图 5 cnt_row, cnt_col 信号波形图

        第二部分:FIFO 缓存相关信号波形的设计与实现
        因为串口每次只输入单字节数据,要想实现多行数据求和,必须要使用 FIFO 对输入数据进行缓存,本实验要实现 3 行数据的求和,需要使用两个 FIFO 进行数据缓存。那么 FIFO 的相关信号的波形就需要设计一下了。
        我们在模块中实例化两个 FIFO ,分别为 fifo_data_inst1 fifo_data_inst2 ,接下来对两个 FIFO 的相关信号进行详细说明。
        两个 FIFO 的输入输出信号端口相同,输入端口有 4 路,输出端口 1 路,共 5 路信号。
        fifo_data_inst1 中,输入时钟信号与串口接收模块的工作时钟相同为系统时钟信号 sys_clk;数据写使能信号为 wr_en1 、写入数据为 data_in1 ,当串口接收模块传入第 0 行数据时,即 cnt_col=0 pi_flag=1 时, wr_en1 信号赋值为高电平,相同条件下, pi_data 赋值data_in1 ,将第 0 行的数据暂存到 fifo_data_inst1 中;当第 1 行数据输入, wr_en1 信号赋值为低电平,data_in1 无数据输入,因为第 1 行的数据要暂存到 fifo_data_inst2 中;自第 2 行数据开始传入到倒数第二行数据传输完成,wr_en1 信号由 dout_flag 信号赋值,当 rd_en 和 wr_en2 信号均为高电平时, dout_flag 信号赋值高电平,其他时刻均为低电平。当 dout_flag 有效时,将 fifo_data_inst2 的读出数据 data_out2 赋值给 data_in1
        fifo_data_inst2 中,输入时钟信号与串口接收模块的工作时钟相同为系统时钟信号 sys_clk; wr_en2 为数据写使能信号, data_in2 为写入数据,自第 1 行数据开始输入到倒数第二行数据输入完成,wr_en2 写使能信号由 pi_flag 信号赋值,时序上滞后 pi_flag 信号 1 个时钟周期,wr_en2 赋值为高电平, fifo_data_inst2 写使能有效,其他时刻写使能无效,写使能信号 wr_en2 有效时,将传入的数据 pi_data 赋值给 data_in2
        rd_en 是两 FIFO 共用的读使能信号,自第 2 行数据开始传入到最后一行数据传输完成,pi_flag 信号赋值给读使能 rd_en ,时序上 rd_en 滞后 pi_flag 信号 1 个时钟周期,其他时刻 rd_en 信号始终保持低电平; data_out1 数据输出受控于 rd_en 读使能信号,读使能有效,data_out1 数据输出,否者保持之前状态,时序上 data_out1 滞后于 rd_en 读使能信号 1 个时钟周期 data_out2 数据输出同样受控于 rd_en 读使能信号,读使能有效, data_out2 数据输出,否者保持之前状态,时序上 data_out2 滞后于 rd_en 读使能信号 1 个时钟周期。
图 6 FIFO 缓存相关信号波形图
        第三部分:数据输出相关信号波形的设计与实现
        两 FIFO 共用的读使能信号 rd_en 有效时,从 FIFO 中分别读取两个待相加数据,两数据与此时输入的数据 pi_data 做求和运算,这里我们需要声明一个新的标志信号做这三个数据求和运算的标志信号。
        以读使能信号 rd_en 滞后一个时钟信号生成求和运算标志信号 po_flag_reg 。当 po_flag_reg 信号为高电平时,将读出两 FIFO 的数据 data_out1 data_out2 与此时输入的 pi_data 做求和运算得出求和结果 po_sum 并输出;同时要输出与 po_sum 信号匹配的数据标志信号 po_flag ,利用 po_flag_reg 信号滞后一个时钟周期生成 po_flag 信号并输出,生成的 po_flag 与与 op_sum 信号同步。上述各信号波形如下图。
图 7 输出数据相关信号波形图

        下面进行代码的编写:

module fifo_sum_ctrl
(
    input  wire       sys_clk   ,    //时钟信号
    input  wire       sys_rst_n ,    //复位信号
    input  wire [7:0] pi_data   ,    //RX传入数据信号
    input  wire       pi_flag   ,    //RX传入标志信号
    
    output reg  [7:0] po_sum    ,    //求和运算后的信号
    output reg        po_flag        //输出数据标志信号
);

//parameter define
parameter CNT_ROW_MAX = 7'd4 ,      //行计数最大值
          CNT_COL_MAX = 7'd5 ;      //列计数最大值
          
//wire define
wire [7:0] data_out1 ;      //fifo1数据输出
wire [7:0] data_out2 ;      //fifo2数据输出

//reg define
reg [6:0] cnt_row     ;     //行计数
reg [6:0] cnt_col     ;     //列计数
reg       wr_en1      ;     //fifo1写使能
reg       wr_en2      ;     //fifo2写使能
reg [7:0] data_in1    ;     //fifo1写数据输入
reg [7:0] data_in2    ;     //fifo2写数据输入
reg       rd_en       ;     //fifo1.fifo2共用的读使能
reg       dout_flag   ;     //控制fifo1,2——84行的写使能
reg       po_flag_reg ;     //输出标志位缓存,rd_en延后一拍得到

//cnt_row:行计数器,计数一行数据个数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_row <= 7'd0 ;
    else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
        cnt_row <= 7'd0 ;
    else if(pi_flag == 1'b1)
        cnt_row <= cnt_row + 1'b1 ;

//cnt_col:列计数器,计数一列数据个数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_col <= 7'd0 ;
    else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1) && (cnt_col == CNT_COL_MAX))
        cnt_col <= 7'd0 ;
    else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1))
        cnt_col <= cnt_col + 1'b1 ;

//wr_en1:fifo1写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_en1 <= 1'b0 ;
    else if((cnt_col == 7'd0) && (pi_flag == 1'b1))
        wr_en1 <= 1'b1 ;    //第0行写入fifo1
    else
        wr_en1 <= dout_flag ;    //2-84行写入fifo1
        
//wr_en2:fifo2写使能信号,高电平有效
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        wr_en2 <= 1'b0 ;
    else if((cnt_col >= 7'd1) && (pi_flag == 1'b1))
        wr_en2 <= 1'b1 ;    //2——CNT_COL_MAX行写入fifo2
    else
        wr_en2 <= 1'b0 ;    
      
//data_in1:fifo1数据输入
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_in1 <= 8'b0 ;
    else if((pi_flag == 1'b1) && (cnt_col == 7'd0))
        data_in1 <= pi_data ;   //第0行数据暂存fifo1中
    else if(dout_flag == 1'b1)
        data_in1 <= data_out2 ; //第2-CNT_COL_MAX-1行时,fifo2读出数据存入fifo1
    else
        data_in1 <= data_in1 ;
        
 //data_in2:fifo2数据输入
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0) 
        data_in2 <= 8'b0 ;
    else if((pi_flag == 1'b1) && (cnt_col >= 7'd1) && (cnt_col <= (CNT_COL_MAX - 1'b1)))
        data_in2 <= pi_data ;
    else
        data_in2 <= data_in2 ;
        
//rd_en:fifo1和fifo2的共用读使能信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_en <= 1'b0 ;
    else if((pi_flag == 1'b1) && (cnt_col >= 7'd2) && (cnt_col <= CNT_COL_MAX))
        rd_en <= 1'b1 ;
    else
        rd_en <= 1'b0 ;
        
//dout_flag:控制2——CNT_COL_MAX-1行wr_en1信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        dout_flag <= 1'b0 ;
    else if((wr_en2 == 1'b1) && (rd_en == 1'b1))
        dout_flag <= 1'b1 ;
    else
        dout_flag <= 1'b0 ;

//po_flag_reg:输出标志位缓存,延后rd_en一拍,控制po_sum信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag_reg <= 1'b0 ;
    else    if(rd_en == 1'b1)
        po_flag_reg <= 1'b1 ;
    else
        po_flag_reg <= 1'b0 ;

//po_flag:输出标志信号,延后输出标志位缓存一拍,与po_sum同步输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_flag <=  1'b0 ;
    else
        po_flag <=  po_flag_reg ;
        
//po_sum:求和数据输出
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        po_sum <= 8'b0 ;
    else if(po_flag_reg == 1'b1)
        po_sum <= data_out1 + data_out2 + pi_data ;
    else
        po_sum <= po_sum ;

//FIFO IP 核的调用    
fifo_data   fifo_data_inst1
(
    .clock  (sys_clk    ),  //input clock
    .data   (data_in1   ),  //input [7:0] data
    .wrreq  (wr_en1     ),  //input wrreq
    .rdreq  (rd_en      ),  //input rdreq

    .q      (data_out1  )   //output [7:0] q
);

fifo_data   fifo_data_inst2
(
    .clock  (sys_clk    ),  //input clock
    .data   (data_in2   ),  //input [7:0] data
    .wrreq  (wr_en2     ),  //input wrreq
    .rdreq  (rd_en      ),  //input rdreq

    .q      (data_out2  )   //output [7:0] q
);
    
endmodule   
    

        信号发送和接收模块这个不再赘述,大家可根据自己以前写的 RS232 模块和上述求和模块一起编写到顶层模块中,RTL视图如图 8 所示:

图 8 RTL视图

        将程序下载到开发板,这里输入五行四列的数据,通过串口调试助手发送并接收,如图 9 所示:

图 9 串口助手收发数据

        与我们的计算结果相符,实验结束。

 

图 10 计算结果

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值