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)