Thank you, Victor, for pointing out the race condition in the sync
fifo.  What I did was add an additional cycle delay on tail0 (calling
it tail4).  This way, the data will change before the tail pointer. 
This may be overkill, but what I decided to do was first add a delay
in in_clock so as to ensure that the delay is sufficient (if I did it
only in out_clock, the edge could arrive too soon), and then add
another delay in out_clock so as to eliminate race conditions in the
logic of the comparators.  Tell me what you think:


// Basic fifo
// Copyright 2005, Timothy Miller
// LGPL license


module sync_fifo(
    reset,

    in_clock,
    data_in,
    enq,
    full,

    out_clock,
    data_out,
    valid_out,
    deq,
    empty
);

parameter fifo_width = 32;
// fifo depth is hard-wired to 16

input in_clock, out_clock, reset;

input [fifo_width-1:0] data_in;
input enq;
output full;
reg full;

output [fifo_width-1:0] data_out;
reg [fifo_width-1:0] data_out;
output valid_out;
reg valid_out;
input deq;
output empty;
reg empty;



reg [fifo_width-1:0] fifo_data [0:15];
reg [3:0] head0, tail0;
reg [3:0] head1, tail1, tail2, tail4, tail5;

wire [3:0] tail3 = next_gray_code(tail2);

// accept input
wire next_full = tail2 == head0;
wire is_full = tail1 == head0;
always @(posedge in_clock or posedge reset) begin
    if (reset) begin
        tail0 <= 0;
        tail1 <= 1;
        tail2 <= 3;
        full <= 0;
    end else begin
        if (!full && enq) begin
            // We can only enqueue when not full
            fifo_data[tail0] <= data_in;
            tail0 <= tail1;
            tail1 <= tail2;
            tail2 <= tail3; //next_gray_code(tail2);

            // We have to compute if it's full on next cycle
            full <= next_full;

            // It might not really be full if we're dequeueing,
            // But we don't really care.  Setting full early
            // will not result in data loss.
        end else begin
            full <= is_full;
        end
    end
end

// Cycle delay for tail to cross clock domain so that data changes
// before tail.  In out_clock domain so as to eliminate races through
// comparators.
always @(posedge in_clock) tail4 <= tail0;
always @(posedge out_clock) tail5 <= tail4;


wire [3:0] head2 = next_gray_code(head1);

// provide output
wire next_empty = head1 == tail5;
wire is_empty = head0 == tail5;
always @(posedge out_clock or posedge reset) begin
    if (reset) begin
        valid_out <= 0;
        data_out <= 0;
        head0 <= 0;
        head1 <= 1;
    end else begin
        // If no valid out or we're dequeueing, we want to grab
        // the next data.  If we're empty, we don't get valid_out,
        // so we don't care if it's garbage.
        if (!valid_out || deq) begin
            data_out <= fifo_data[head0];
        end

        if (!valid_out || deq) begin
            // Yes, can have valid_out and empty at the same time.
            // empty just means there are 16 entries free.
            empty <= next_empty;
        end else begin
            empty <= is_empty;
        end
        // In either case, empty could be 'wrong' if an enq happens at
        // the same time.  Fortunately, data_out functions as a 17th
        // entry, and of course, you should never design anything that
        // assumes 'empty' means you can blindly euqueue 16 entries
        // (ie. without paying attention to 'full').

        if (!is_empty) begin
            if (!valid_out || deq) begin
                head0 <= head1;
                head1 <= head2; //next_gray_code(head1);
            end
            valid_out <= 1;
        end else begin
            if (deq) valid_out <= 0;
        end
    end
end



// This should turn into four LUT RAMs, which is the best you can get
// from an FPGA for this function.
function [3:0] next_gray_code;
input [3:0] I;
begin
    case (I)
         0: next_gray_code =  1;
         1: next_gray_code =  3;
         3: next_gray_code =  2;
         2: next_gray_code =  6;
         6: next_gray_code =  7;
         7: next_gray_code =  5;
         5: next_gray_code =  4;
         4: next_gray_code = 12;
        12: next_gray_code = 13;
        13: next_gray_code = 15;
        15: next_gray_code = 14;
        14: next_gray_code = 10;
        10: next_gray_code = 11;
        11: next_gray_code =  9;
         9: next_gray_code =  8;
         8: next_gray_code =  0;
    endcase
end
endfunction

endmodule

_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to