本文调用vivado中ip核的两个standard fifo,通过调整使能信号(wr_en,rd_en)的时序和data_in(与in_1,in_2),data_out(与out_1,out_2)的接通,实现乒乓fifo传输数据流的无缝输入输出,不丢失任何一位数据。

        本项目的难点:

        1、写入:必须在fifo1停止写入数据之后,下一个时钟上升沿马上把数据写入fifo2,但是由于standard fifo的运行模式,若我们把使能信号简单地分配给统一的状态机,会出现读/写不全、重复读/写等问题。

        在almost_full_1信号拉高之后,下一个时钟上升沿fifo1将写入最后一位数据,但state仍旧未切换,等state切换再拉高wr_en_2会使得至少一位数据发生丢失因此我们需要在almost_full_1信号拉高之后,将wr_en_2提前拉高,并将wr_en_1拉低,保证对data_in数据的完整采集,且不会出现一个数据写入到两个fifo的现象。如下图所示:fifo_1最后一位写入的是“0”,此后将data_in新输入的数据“1”直接写入fifo_2。

        特别声明:wr_ack信号拉高前一个周期的数据能够写入,而拉低前的最后一位数据是无法写入fifo(因为该信号判断流程为:尝试写入——写入成功/不成功——拉高/拉低,属于“滞后”标记位)

vivado中调用python3解释器_数据

       

       既然读使能信号wr_en需要提前打开,那么data_in所进入的通道(in_1\in_2)也应该随着使能信号wr_en同时切换(注意:是同时切换,wr_en一变化,通道同时就切换,是同时操作,并不是滞后一个时期),如下图所示:

vivado中调用python3解释器_sed_02

 

2、读出:

        读出操作相对而言就比较简单,在对写入操作进行上述处理后,rd_en只需在n_state切换的时刻将rd_en进行切换,即可正常读出数据。

        data_out根据rd_en的切换来切换即可,时序不需要多做要求,自然顺着读就行,不会丢数据,仅会滞后几个周期。

        特别声明:只要数据在valid信号拉高的“框内"即可成功读出(因为该信号判断流程为:valid拉高/拉低——能够/不能成功读出——成功写入/没写成功,属于“提前”标记位)

3、ip核的配置

        看图:        

vivado中调用python3解释器_上升沿_03

vivado中调用python3解释器_vivado中调用python3解释器_04

vivado中调用python3解释器_上升沿_05

 

源代码:

`timescale 1ns / 1ps
module fifo_pp_myself(
    input clk,
    input _rst,
    input wr_en,
    input       [9:0]data_in,
    output  reg [9:0]data_out
    );
  reg [3:0]c_state;
  reg [3:0]n_state;
  reg  wr_en_1;
  reg  rd_en_1;
  reg  wr_en_2;
  reg  rd_en_2;
  reg  [9:0]in_1;
  reg  [9:0]in_2;
  wire [9:0]out_1;
  wire [9:0]out_2;
  wire full_1;
  wire full_2;
  wire empty_1;
  wire empty_2;
  wire [9:0]data_out_r;
  wire wr_ack_1,wr_ack_2;
  wire valid_1,valid_2;
  wire almost_full_1,almost_full_2;
  wire almost_empty_1,almost_empty_2;
   parameter
        idle    = 4'b0000,
        start   = 4'b0010,
        w1_r2   = 4'b1000,
        r1_w2   = 4'b0100;
  /********例话两个标准FIFO*********/              
        fifo_generator_0 fifo_1(
        .clk(clk),
        .srst(~_rst),
        .din(in_1),
        .full(full_1),
        .almost_full(almost_full_1),
        .wr_en(wr_en_1),
        .empty(empty_1),
        .almost_empty(almost_empty_1),
        .dout(out_1),
        .rd_en(rd_en_1),
        .wr_ack(wr_ack_1),
        .valid(valid_1)
 );
fifo_generator_0 fifo_2(
    .clk(clk),
    .srst(~_rst),
    .din(in_2),
    .full(full_2),
    .almost_full(almost_full_2),
    .wr_en(wr_en_2),
    .empty(empty_2),
    .almost_empty(almost_empty_2),
    .dout(out_2),
    .rd_en(rd_en_2),
     .wr_ack(wr_ack_2),
     .valid(valid_2)
  );
   /*******状态连续*********/
   always@(posedge clk or negedge _rst)
   if(!_rst)
        c_state <= idle;
   else
        c_state <= n_state;
  /********状态机**********/
  always@(posedge clk or negedge _rst)
    case(c_state)
        idle:
            if(wr_en)
                n_state <= start;
            else
                n_state <= idle;
        start:
            if(full_1)
                n_state <= r1_w2;
            else
                n_state <= start;
        r1_w2:
            if(empty_1 | full_2)
                n_state <= w1_r2;
            else
                n_state <= r1_w2;
        w1_r2:
            if(empty_2 | full_1)
                n_state <= r1_w2;
            else
                n_state <= w1_r2;
        default:n_state <= idle;
        endcase
   /******使能信号*******/
    always@(posedge clk or negedge _rst)
    begin
        case(n_state)
            idle:
                    begin
                        wr_en_1 <= 1'b0;
                        rd_en_1 <= 1'b0;
                        wr_en_2 <= 1'b0;              
                        rd_en_2 <= 1'b0;
                    end
            start:
                    begin
                        if(almost_full_1)//提前打开下一个写入通道的开关,防止丢交接时候的两个数据
                            begin
                                 wr_en_1 <= 1'b0;
                                 wr_en_2 <= 1'b1;
                            end 
                        else
                            begin
                                 wr_en_1 <= 1'b1;
                                 rd_en_1 <= 1'b0;
                                 wr_en_2 <= 1'b0;              
                                 rd_en_2 <= 1'b0;
                            end 
                   end
            r1_w2:
                   begin
                           if(almost_full_2)
                              begin
                                  wr_en_1 <= 1'b1;
                                  wr_en_2 <= 1'b0;
                              end
                           
                           else
                              begin
                                  wr_en_1 <= 1'b0;
                                  rd_en_1 <= 1'b1;
                                  wr_en_2 <= 1'b1;              
                                  rd_en_2 <= 1'b0;
                              end
                   end
           w1_r2:
                 begin
                           if(almost_full_1)
                              begin
                                  wr_en_1 <= 1'b0;
                                  wr_en_2 <= 1'b1;
                              end
                           
                           else
                              begin
                                  wr_en_1 <= 1'b1;
                                  rd_en_1 <= 1'b0;
                                  wr_en_2 <= 1'b0;              
                                  rd_en_2 <= 1'b1;
                              end
                   end
     endcase
     end
    
   
  /***写数据的交互****/
  always@(posedge clk or negedge _rst)
  case(n_state)
    start: in_1 <= data_in; 
  endcase
  always@(posedge clk or negedge _rst)
  case(c_state)
    start:begin
            if(almost_full_1)begin
                in_2 <= data_in;
                in_1 <= in_1; end
            else
                in_1 <= data_in;
          end
    r1_w2:begin//4
                if(almost_full_2)begin                
                        in_1 <= data_in;
                        in_2 <= in_2;end
                else
                    begin 
                        in_2 <= data_in; 
                        //data_out <= out_1;
                     end
          end
    w1_r2://8
            begin
                if(almost_full_1)begin
                    in_2 <= data_in;
                    in_1 <= in_1;end 
                else
                    begin 
                        in_1 <= data_in; 
                       // data_out <= out_2;
                     end
          end
    endcase
  /*******data读出只需要跟读使能匹配就行*************/
   always@(posedge clk or negedge _rst)
   if(!_rst)
        data_out <= 0;
   else if(rd_en_1)
        data_out <= out_1;
   else 
        data_out <= out_2;             
endmodule

tb文件:

`timescale 1ns / 1ps
module fifo_pp_myself_tb();
    reg     clk;         
    reg     _rst;        
    reg     wr_en;       
    reg     [9:0]data_in;
    wire    [9:0]data_out;
fifo_pp_myself fifo_pp_myself(
     .clk       (clk     ),
     ._rst      (_rst    ),
     .wr_en     (wr_en   ),
     .data_in   (data_in ),
     .data_out  (data_out)
    );
    initial clk = 1;
    always#10 clk = ~clk;
    always#20 data_in = data_in + 1'b1;
    
    initial begin
        _rst = 1'b1;
        #20;
        _rst = 1'b0;
//        wr_en = 1'b1;
        #200;
        wr_en = 1'b1;//开启读使能之后再拉高复位,这样第一个输入的数据也能读出来
        #20;
        _rst = 1'b1;
        //#500;      
        data_in = 10'b0;
        #200000;
        $stop;
    end
endmodule

仿真图:

vivado中调用python3解释器_fpga开发_06

 

vivado中调用python3解释器_sed_07

         根据细节图,在状态交接处,数据仍是连续不间断,且不会出现重复读写同一数据的情况。

         根据全局图,可以看出每次写入fifo时的最后一位都是0,说明每个周期读取次数也并没有出现问题(多读或者少读,长长的那一位都不会是0,可能是3,5,7)

        结论:乒乓fifo操作基本实现,目前并未发现有bug。