OpenCores
URL https://opencores.org/ocsvn/nysa_sata/nysa_sata/trunk

Subversion Repositories nysa_sata

[/] [nysa_sata/] [trunk/] [rtl/] [generic/] [ppfifo.v] - Rev 4

Go to most recent revision | Compare with Previous | Blame | View Log

/*
Distributed under the MIT licesnse.
Copyright (c) 2011 Dave McCoy (dave.mccoy@cospandesign.com)
 
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
 
`timescale 1ns/1ps
 
//XXX: NOTE: All counts are 24bits long, this could be a parameter in the future
 
 
module ppfifo
#(parameter     DATA_WIDTH    = 8,
                ADDRESS_WIDTH = 4
)(
 
  //universal input
  input                             reset,
 
  //write side
  input                             write_clock,
  output reg  [1:0]                 write_ready,
  input       [1:0]                 write_activate,
  output      [23:0]                write_fifo_size,
  input                             write_strobe,
  input       [DATA_WIDTH - 1: 0]   write_data,
  output                            starved,
 
  //read side
  input                             read_clock,
  input                             read_strobe,
  output reg                        read_ready,
  input                             read_activate,
  output reg  [23:0]                read_count,
  output      [DATA_WIDTH - 1: 0]   read_data,
 
  output                            inactive
 
);
 
localparam FIFO_DEPTH = (1 << ADDRESS_WIDTH);
 
//Local Registers/Wires
 
//Write Side
wire                        ppfifo_ready;  // The write side only needs to
                                           // know were ready if we don't
                                           // write anything read won't start
wire  [ADDRESS_WIDTH: 0]    addr_in;       // Actual address to the BRAM
reg   [ADDRESS_WIDTH - 1: 0]write_address;
reg                         r_wselect;      // Select the FIFO to work with
reg                         write_enable;  // Enable a write to the BRAM
 
reg   [1:0]                 wcc_read_ready;// Tell read side a FIFO is ready
wire  [1:0]                 wcc_read_done;
                                            // write status of the read
                                            // available
reg                         wcc_tie_select; //because it's possible if the read
                                            //side is slower than the write
                                            //side it might be unknown which
                                            //side was selected first, so use
                                            //this signal to break ties
 
reg   [23:0]                w_count[1:0];   // save the write count for the read
                                            // side
reg                         w_empty[1:0];
reg                         w_reset;        //write side reset
reg   [4:0]                 w_reset_timeout;
wire                        ready;
 
//Read Side
wire  [ADDRESS_WIDTH: 0]    addr_out;     //Actual address to the BRAM
reg                         r_reset;
reg   [4:0]                 r_reset_timeout;
 
reg   [ADDRESS_WIDTH - 1: 0]r_address;    //Address to access a bank
reg                         r_rselect;     //Select a bank (Select a FIFO)
 
wire  [1:0]                 rcc_read_ready;
                                          // Write side says X is ready
reg   [1:0]                 rcc_read_done;// Tell write side X is ready
wire                        rcc_tie_select;
                                          //If there is a tie, then this will
                                          //break it
 
reg   [23:0]                r_size[1:0];  // Size of FX read
 
reg   [1:0]                 r_ready;      //FIFO is ready
reg   [1:0]                 r_wait;       //Waiting for write side to send data
reg   [1:0]                 r_activate;   //Controls which FIFO is activated
 
reg                         r_next_fifo;  //If both FIFOs are availalbe use this
 
reg   [1:0]                 r_pre_activate; //Used to delay the clock cycle by
                                            //one when the user activates the
                                            //FIFO
 
reg                         r_pre_strobe;
reg                         r_pre_read_wait;//Wait an extra cycle so the registered data has a chance to set
                                            //the data to be registered
wire  [DATA_WIDTH - 1: 0]   w_read_data;    //data from the read FIFO
reg   [DATA_WIDTH - 1: 0]   r_read_data;    //data from the read FIFO
 
 
 
//assign  r_wselect           = (write_activate == 2'b00) ? 1'b0 :
//                              (write_activate == 2'b01) ? 1'b0 :
//                              (write_activate == 2'b10) ? 1'b1 :
//                              reset ?                     1'b0 :
//                              r_wselect;
//                            //I know this can be shortened down but it's more
//                            //readible thi way
 
assign  write_fifo_size     = FIFO_DEPTH;
 
assign  addr_in             = {r_wselect, write_address};
//assign  write_enable        = (write_activate > 0) && write_strobe;
assign  ppfifo_ready        = !(w_reset || r_reset);
assign  ready               = ppfifo_ready;
 
//assign  wcc_tie_select      = (wcc_read_ready == 2'b00) ? 1'b0 :
//                              (wcc_read_ready == 2'b01) ? 1'b0 :
//                              (wcc_read_ready == 2'b10) ? 1'b1 :
//                              wcc_tie_select;
                                            // If the first FIFO is ready,
                                            // then both FIFOs are ready then
                                            // keep the first FIFO
 
assign  addr_out            = {r_rselect, r_address};
 
 
//Debug
//wire  [23:0]                debug_f0_w_count;
//wire  [23:0]                debug_f1_w_count;
 
//wire  [23:0]                debug_f0_r_size;
//wire  [23:0]                debug_f1_r_size;
 
//wire  [23:0]                debug_f0_r_count;
//wire  [23:0]                debug_f1_r_count;
 
//assign  debug_f0_w_count    = w_count[0];
//assign  debug_f1_w_count    = w_count[1];
 
//assign  debug_f0_r_size     = r_size[0];
//assign  debug_f1_r_size     = r_size[1];
 
assign  inactive            = (w_count[0] == 0) &&
                              (w_count[1] == 0) &&
                              (write_ready == 2'b11) &&
                              (!write_strobe);
 
 
assign  read_data           = (r_pre_strobe) ? w_read_data : r_read_data;
 
 
//Submodules
blk_mem #(
  .DATA_WIDTH(DATA_WIDTH),
  .ADDRESS_WIDTH(ADDRESS_WIDTH + 1)
) fifo0 (
  //Write
  .clka     (write_clock        ),
  .wea      (write_enable       ), //This may just be replaced with write activate
  .dina     (write_data         ),
  .addra    (addr_in            ),
 
  .clkb     (read_clock         ),
  .doutb    (w_read_data        ),
  .addrb    (addr_out           )
);
 
//W - R FIFO 0
cross_clock_enable ccwf0 (
  .rst      (reset              ),
  .in_en    (wcc_read_ready[0]  ),
 
  .out_clk  (read_clock         ),
  .out_en   (rcc_read_ready[0]  )
);
//W - R FIFO 1
cross_clock_enable ccwf1 (
  .rst      (reset              ),
  .in_en    (wcc_read_ready[1]  ),
 
  .out_clk  (read_clock         ),
  .out_en   (rcc_read_ready[1]  )
 
);
 
//W - R Tie Select
cross_clock_enable ccts (
  .rst      (reset              ),
  .in_en    (wcc_tie_select     ),
 
  .out_clk  (read_clock         ),
  .out_en   (rcc_tie_select     )
);
 
//R - W FIFO 0
cross_clock_enable ccrf0 (
  .rst      (reset              ),
  .in_en    (rcc_read_done[0]   ),
 
  .out_clk  (read_clock         ),
  .out_en   (wcc_read_done[0]   )
);
//R - W FIFO 1
cross_clock_enable ccrf1 (
  .rst      (reset              ),
  .in_en    (rcc_read_done[1]   ),
 
  .out_clk  (read_clock         ),
  .out_en   (wcc_read_done[1]   )
);
 
//R - W Reset
cross_clock_enable cc_starved(
  .rst      (reset                         ),
  .in_en    (!read_ready && !read_activate ),
 
  .out_clk  (write_clock                   ),
  .out_en   (starved                       )
);
 
 
 
 
//asynchronous logic
always @ (*) begin
  case (wcc_read_ready)
    2'b00: begin
      wcc_tie_select  = 1'b0;
    end
    2'b01: begin
      wcc_tie_select  = 1'b0;
    end
    2'b10: begin
      wcc_tie_select  = 1'b1;
    end
    default: begin
      wcc_tie_select  = 1'b0;
    end
  endcase
end
always @ (*) begin
  case (write_activate)
    2'b00: begin
      r_wselect   = 1'b0;
    end
    2'b01: begin
      r_wselect   = 1'b0;
    end
    2'b10: begin
      r_wselect   = 1'b1;
    end
    default: begin
      r_wselect   = 1'b0;
    end
  endcase
end
 
always @ (*) begin
  if (write_activate > 0 && write_strobe) begin
    write_enable  = 1'b1;
  end
  else begin
    write_enable  = 1'b0;
  end
end
//synchronous logic
 
//Reset Logic
always @ (posedge write_clock) begin
  if (reset) begin
    w_reset         <=  1;
    w_reset_timeout <=  0;
  end
  else begin
    if (w_reset && (w_reset_timeout < 5'h4)) begin
      w_reset_timeout <=  w_reset_timeout + 5'h1;
    end
    else begin
      w_reset       <=  0;
    end
  end
end
 
always @ (posedge read_clock) begin
  if (reset) begin
    r_reset           <=  1;
    r_reset_timeout   <=  0;
  end
  else begin
    if (r_reset && (r_reset_timeout < 5'h4)) begin
      r_reset_timeout <= r_reset_timeout + 5'h1;
    end
    else begin
      r_reset         <=  0;
    end
  end
end
 
//---------------Write Side---------------
initial begin
  write_address     = 0;
 
  wcc_read_ready    = 2'b00;
  write_ready       = 2'b00;
  w_reset           = 1;
  w_reset_timeout   = 0;
  write_enable      = 0;
  r_wselect         = 0;
  wcc_tie_select    = 0;
end
 
always @ (posedge write_clock) begin
  if (reset) begin
    write_ready     <=  0;
  end
  else if (ready) begin
    //Logic for the Write Enable
    if (write_activate[0] && write_strobe) begin
      write_ready[0] <=  1'b0;
    end
    if (write_activate[1] && write_strobe) begin
      write_ready[1] <=  1'b0;
    end
 
    if (!write_activate[0] && (w_count[0] == 0) && wcc_read_done[0]) begin
      //FIFO 0 is not accessed by the read side, and user has not activated
      write_ready[0]  <=  1;
    end
    if (!write_activate[1] && (w_count[1] == 0) && wcc_read_done[1]) begin
      //FIFO 1 is not accessed by the read side, and user has not activated
      write_ready[1]  <=  1;
    end
 
    //When the user is finished reading the ready signal might go high
    //I SHOULD MAKE A CONDITION WHERE THE WRITE COUND MUST BE 0
  end
end
 
always @ (posedge write_clock) begin
  if (reset) begin  //Asynchronous Reset
    wcc_read_ready  <=  2'b00;
    write_address   <=  0;
    w_count[0]      <=  24'h0;
    w_count[1]      <=  24'h0;
  end
  else begin
    if (write_activate > 0 && write_strobe) begin
      write_address <=  write_address + 1;
      if (write_activate[0]) begin
        w_count[0]  <=  w_count[0] + 1;
      end
      if (write_activate[1]) begin
        w_count[1]  <=  w_count[1] + 1;
      end
    end
    if (write_activate == 0) begin
      write_address     <=  0;
      if (w_count[0] > 0) begin
        wcc_read_ready[0]  <=  1;
      end
      if (w_count[1] > 0) begin
        wcc_read_ready[1]  <=  1;
      end
    end
    //I can't reset the w_count until the read side has indicated that it
    //is done but that may be mistriggered when the write side finishes
    //a write. So how do
 
    //Only reset the write count when the done signal has been de-asserted
 
    //Deassert w_readX_ready when we see the read has de-asserted r_readX_done
    if (!wcc_read_done[0]) begin
      //Only reset write count when the read side has said it's busy so it
      //must be done reading
      w_count[0]        <=  0;
      wcc_read_ready[0] <=  0;
    end
    if (!wcc_read_done[1]) begin
      w_count[1]        <=  0;
      wcc_read_ready[1] <=  0;
    end
  end
end
 
 
//---------------Read Side---------------
initial begin
  r_rselect       = 0;
  r_address       = 0;
 
  rcc_read_done   = 2'b00;
 
  r_size[0]       = 24'h0;
  r_size[1]       = 24'h0;
 
  r_wait          = 2'b11;
  r_ready         = 2'b00;
  r_activate      = 2'b00;
  r_pre_activate  = 0;
 
  r_next_fifo     = 0;
  r_reset         = 1;
  r_reset_timeout = 0;
  read_ready      = 0;
  r_read_data     = 32'h0;
  r_pre_read_wait = 0;
end
 
always @ (posedge read_clock) begin
  if (reset) begin  //Asynchronous Reset
    r_rselect                       <=  0;
    r_address                       <=  0;
    rcc_read_done                   <=  2'b11;
    read_count                      <=  0;
    r_activate                      <=  2'b00;
    r_pre_activate                  <=  2'b00;
    r_pre_read_wait                 <=  0;
 
    r_size[0]                       <=  24'h0;
    r_size[1]                       <=  24'h0;
 
    //are these signals redundant?? can I just use the done?
    r_wait                          <=  2'b11;
 
    r_ready                         <=  2'b00;
 
    r_next_fifo                     <=  0;
    r_read_data                     <=  0;
 
  end
  else begin
    r_pre_strobe                    <=  read_strobe;
    //Handle user enable and ready
    if (!read_activate && !r_pre_activate) begin
      //User has not activated the read side
 
      //Prepare the read side
      //Reset the address
      if (r_activate == 0) begin
        read_count                      <=  0;
        r_address                       <=  0;
        r_pre_read_wait                 <=  0;
        if (r_ready > 0) begin
          //This goes to one instead of activate
          //Output select
          //read_ready                  <=  1;
 
          if (r_ready[0] && r_ready[1]) begin
            //$display ("Tie");
            //Tie
            r_rselect                   <=  r_next_fifo;
            r_pre_activate[r_next_fifo] <=  1;
            r_pre_activate[~r_next_fifo]<=  0;
            r_next_fifo                 <=  ~r_next_fifo;
            read_count                  <=  r_size[r_next_fifo];
          end
          else begin
            //Only one side is ready
            if (r_ready[0]) begin
              //$display ("select 0");
              r_rselect                 <=  0;
              r_pre_activate[0]         <=  1;
              r_pre_activate[1]         <=  0;
              r_next_fifo               <=  1;
              read_count                <=  r_size[0];
            end
            else begin
              //$display ("select 1");
              r_rselect                 <=  1;
              r_pre_activate[0]         <=  0;
              r_pre_activate[1]         <=  1;
              r_next_fifo               <=  0;
              read_count                <=  r_size[1];
            end
          end
        end
      end
      //User has finished reading something
      else if (r_activate[r_rselect] && !r_ready[r_rselect]) begin
        r_activate[r_rselect]          <=  0;
        rcc_read_done[r_rselect]       <=  1;
      end
    end
    else begin
      if ((r_pre_activate > 0) && r_pre_read_wait) begin
        read_ready                      <=  1;
      end
 
 
      if (r_activate) begin
        read_ready                    <=  0;
        //User is requesting an accss
        //Handle read strobes
        //Only handle strobes when we are enabled
        r_ready[r_rselect]            <=  0;
      end
 
      //XXX: There should be a better way to handle these edge conditions
      if (!r_activate && r_pre_read_wait) begin
        r_read_data                     <=  w_read_data;
        r_address                       <=  r_address + 1;
        r_activate                      <=  r_pre_activate;
        r_pre_activate                  <=  0;
      end
      else if (!r_pre_read_wait) begin
        r_pre_read_wait                 <=  1;
      end
 
      if (read_strobe && (r_address < (r_size[r_rselect] + 1))) begin
        //Increment the address
        r_read_data                     <=  w_read_data;
        r_address                       <=  r_address + 1;
      end
      if (r_pre_strobe && !read_strobe) begin
        r_read_data                     <=  w_read_data;
      end
    end
    if (!rcc_read_ready[0] && !r_ready[0] && !r_activate[0]) begin
      r_wait[0]                       <=  1;
    end
    if (!rcc_read_ready[1] && !r_ready[1] && !r_activate[1]) begin
      r_wait[1]                       <=  1;
    end
 
 
    //Check if the write side has sent over some data
    if (rcc_read_ready > 0) begin
      if ((r_wait == 2'b11) && (rcc_read_ready == 2'b11) && (w_count[0] > 0) && (w_count[1] > 0)) begin
        //An ambiguous sitution that can arrise if the read side is much
        //slower than the write side, both sides can arrise seemingly at the
        //same time, so there needs to be a tie breaker
        //$display ("Combo breaker goes to: %h", rcc_tie_select);
        r_next_fifo         <= rcc_tie_select;
      end
 
 
 
 
      if (r_wait[0] && rcc_read_ready[0]) begin
        //$display ("write has send over data t othe 0 side");
        //Normally it would not be cool to transfer data over a clock domain
        //but the w_count[x] is stable before the write side sends over a write
        //strobe
        if (w_count[0] > 0) begin
          //Only enable when count > 0
          if ((r_activate == 0) && (!rcc_read_ready[1])) begin
              //$display ("select 0");
              r_next_fifo   <=  0;
          end
          r_size[0]         <=  w_count[0];
          r_ready[0]        <=  1;
          r_wait[0]         <=  0;
          rcc_read_done[0]  <=  0;
        end
      end
 
      if (r_wait[1] && rcc_read_ready[1]) begin
        //$display ("write has send over data t othe 1 side");
        //Write side has sent some data over
        if (w_count[1] > 0) begin
         if ((r_activate == 0) && (!rcc_read_ready[0])) begin
              //$display ("select 1");
              r_next_fifo   <=  1;
          end
          r_size[1]         <=  w_count[1];
          r_ready[1]        <=  1;
          r_wait[1]         <=  0;
          rcc_read_done[1]  <=  0;
        end
      end
    end
  end
end
 
endmodule
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.