Hello all,

Since the topics seem to have turned to hardware related matters, I decided to 
see if I could contribute a bit. :) I've done high-speed ASIC/FPGA design for 
the past 8 years now for a living, so I might be able to make a few useful 
comments here and there.

I'd like to start with the FIFO. Timothy and I already have a few emails about 
this, but he said it would be good to put it on the mailinglist so everybody 
can chip in. I'll post the code as it was sent to me below, and see if I can 
put some questions/comments in between using -- (which incidently is VHDL 
comment syntax, which is what I usually design in. My verilog knowledge is 
still virtually nonexisting); just search for it to skip most of what you've 
already seen.


// Async fifo
// Input and output can be in different clock domains
// Copyright 2005, Timothy Miller
// LGPL license

-- Why is empty on the output bus? Is another block going to use this or will 
-- they only consider valid_out in accordance with 
-- http://opengraphics.gitk.com/fifo_handshake.gif? I can think of valid
-- reasons for wanting to use this, but it's not in the interface spec.
module async_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;


-- I'd assume that head would be the write pointer, and tail the read pointer, 
-- but since you use tail for writing data, I guess my first assumption is
-- wrong. Is head the read and tail the write pointer?
-- Also: I'd advise using useful nametags, like _1d, _2d for once delayed,
-- twice delayed, etc., _c2c for crossing clock areas, and _meta for meta 
-- stability FFs. This will also help you locate these FFs for timing analysis
-- later.
reg [fifo_width-1:0] fifo_data [0:15];
reg [3:0] head0, tail0, head4, head3;
reg [3:0] head1, tail1, tail2, tail4, tail5;

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


// Fix metastability of head0 being clocked into in_clock domain.
always @(posedge in_clock) head3 <= head0;
always @(posedge in_clock) head4 <= head4;
-- I'm not quite sure what you're trying to do here. Is this the correct code 
-- or am I misunderstanding something here? You're assigning head4 to head4?


// accept input
wire next_full = tail2 == head4;
wire is_full = tail1 == head4;
always @(posedge in_clock or posedge reset) begin
-- This is an asynchronous reset; something which in my opinion should be 
-- banned from all designs. It's caused more problems than it's solved if you 
-- ask me.
    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
-- I'm sure I'm missing something again. You're writing with the tail pointer, 
-- but tail is also stored multiple times? Why? Seems to me like all you need 
-- is the current write pointer, and the next write pointer.
            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


// Fix metastability of tail0 being clocked into out_clock domain.
// As a side-effect, eliminate race condition between tail0 and
// memory data.
always @(posedge out_clock) tail4 <= tail0;
always @(posedge out_clock) tail5 <= tail4;
-- This makes more sense. tail0 is from the in_clock domain, and is twice 
-- clocked up for meta stability. tail5 will be used in the comparison.

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 data_out is 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