Files
zjui-ece385-final/qsys/synthesis/submodules/altera_avalon_dc_fifo.v

731 lines
25 KiB
Verilog

// $File: //acds/rel/18.1std/ip/sopc/components/altera_avalon_dc_fifo/altera_avalon_dc_fifo.v $
// $Revision: #1 $
// $Date: 2018/07/18 $
// $Author: psgswbuild $
//-------------------------------------------------------------------------------
// Description: Dual clocked single channel FIFO with fill levels and status
// information.
// ---------------------------------------------------------------------
`timescale 1 ns / 100 ps
//altera message_off 10036 10858 10230 10030 10034
module altera_avalon_dc_fifo(
in_clk,
in_reset_n,
out_clk,
out_reset_n,
// sink
in_data,
in_valid,
in_ready,
in_startofpacket,
in_endofpacket,
in_empty,
in_error,
in_channel,
// source
out_data,
out_valid,
out_ready,
out_startofpacket,
out_endofpacket,
out_empty,
out_error,
out_channel,
// in csr
in_csr_address,
in_csr_write,
in_csr_read,
in_csr_readdata,
in_csr_writedata,
// out csr
out_csr_address,
out_csr_write,
out_csr_read,
out_csr_readdata,
out_csr_writedata,
// streaming in status
almost_full_valid,
almost_full_data,
// streaming out status
almost_empty_valid,
almost_empty_data,
// (internal, experimental interface) space available st source
space_avail_data
);
// ---------------------------------------------------------------------
// Parameters
// ---------------------------------------------------------------------
parameter SYMBOLS_PER_BEAT = 1;
parameter BITS_PER_SYMBOL = 8;
parameter FIFO_DEPTH = 16;
parameter CHANNEL_WIDTH = 0;
parameter ERROR_WIDTH = 0;
parameter USE_PACKETS = 0;
parameter USE_IN_FILL_LEVEL = 0;
parameter USE_OUT_FILL_LEVEL = 0;
parameter WR_SYNC_DEPTH = 2;
parameter RD_SYNC_DEPTH = 2;
parameter STREAM_ALMOST_FULL = 0;
parameter STREAM_ALMOST_EMPTY = 0;
parameter BACKPRESSURE_DURING_RESET = 0;
// optimizations
parameter LOOKAHEAD_POINTERS = 0;
parameter PIPELINE_POINTERS = 0;
// experimental, internal parameter
parameter USE_SPACE_AVAIL_IF = 0;
localparam ADDR_WIDTH = log2ceil(FIFO_DEPTH);
localparam DEPTH = 2 ** ADDR_WIDTH;
localparam DATA_WIDTH = SYMBOLS_PER_BEAT * BITS_PER_SYMBOL;
localparam EMPTY_WIDTH = log2ceil(SYMBOLS_PER_BEAT);
localparam PACKET_SIGNALS_WIDTH = 2 + EMPTY_WIDTH;
localparam PAYLOAD_WIDTH = (USE_PACKETS == 1) ?
2 + EMPTY_WIDTH + DATA_WIDTH + ERROR_WIDTH + CHANNEL_WIDTH:
DATA_WIDTH + ERROR_WIDTH + CHANNEL_WIDTH;
// ---------------------------------------------------------------------
// Input/Output Signals
// ---------------------------------------------------------------------
input in_clk;
input in_reset_n;
input out_clk;
input out_reset_n;
input [DATA_WIDTH - 1 : 0] in_data;
input in_valid;
input in_startofpacket;
input in_endofpacket;
input [((EMPTY_WIDTH > 0) ? EMPTY_WIDTH - 1 : 0) : 0] in_empty;
input [((ERROR_WIDTH > 0) ? ERROR_WIDTH - 1 : 0) : 0] in_error;
input [((CHANNEL_WIDTH > 0) ? CHANNEL_WIDTH - 1 : 0) : 0] in_channel;
output in_ready;
output [DATA_WIDTH - 1 : 0] out_data;
output reg out_valid;
output out_startofpacket;
output out_endofpacket;
output [((EMPTY_WIDTH > 0) ? EMPTY_WIDTH - 1 : 0) : 0] out_empty;
output [((ERROR_WIDTH > 0) ? ERROR_WIDTH - 1 : 0) : 0] out_error;
output [((CHANNEL_WIDTH > 0) ? CHANNEL_WIDTH - 1 : 0) : 0] out_channel;
input out_ready;
input in_csr_address;
input in_csr_read;
input in_csr_write;
input [31 : 0] in_csr_writedata;
output reg [31 : 0] in_csr_readdata;
input out_csr_address;
input out_csr_read;
input out_csr_write;
input [31 : 0] out_csr_writedata;
output reg [31 : 0] out_csr_readdata;
output reg almost_full_valid;
output reg almost_full_data;
output reg almost_empty_valid;
output reg almost_empty_data;
output [ADDR_WIDTH : 0] space_avail_data;
// ---------------------------------------------------------------------
// Memory Pointers
// ---------------------------------------------------------------------
(* ramstyle="no_rw_check" *) reg [PAYLOAD_WIDTH - 1 : 0] mem [DEPTH - 1 : 0];
wire [ADDR_WIDTH - 1 : 0] mem_wr_ptr;
wire [ADDR_WIDTH - 1 : 0] mem_rd_ptr;
reg [ADDR_WIDTH : 0] in_wr_ptr;
reg [ADDR_WIDTH : 0] in_wr_ptr_lookahead;
reg [ADDR_WIDTH : 0] out_rd_ptr;
reg [ADDR_WIDTH : 0] out_rd_ptr_lookahead;
// ---------------------------------------------------------------------
// Internal Signals
// ---------------------------------------------------------------------
wire [ADDR_WIDTH : 0] next_out_wr_ptr;
wire [ADDR_WIDTH : 0] next_in_wr_ptr;
wire [ADDR_WIDTH : 0] next_out_rd_ptr;
wire [ADDR_WIDTH : 0] next_in_rd_ptr;
reg [ADDR_WIDTH : 0] in_wr_ptr_gray /*synthesis ALTERA_ATTRIBUTE = "SUPPRESS_DA_RULE_INTERNAL=D102" */;
wire [ADDR_WIDTH : 0] out_wr_ptr_gray;
reg [ADDR_WIDTH : 0] out_rd_ptr_gray /*synthesis ALTERA_ATTRIBUTE = "SUPPRESS_DA_RULE_INTERNAL=D102" */;
wire [ADDR_WIDTH : 0] in_rd_ptr_gray;
reg [ADDR_WIDTH : 0] out_wr_ptr_gray_reg;
reg [ADDR_WIDTH : 0] in_rd_ptr_gray_reg;
reg full;
reg empty;
wire [PAYLOAD_WIDTH - 1 : 0] in_payload;
reg [PAYLOAD_WIDTH - 1 : 0] out_payload;
reg [PAYLOAD_WIDTH - 1 : 0] internal_out_payload;
wire [PACKET_SIGNALS_WIDTH - 1 : 0] in_packet_signals;
wire [PACKET_SIGNALS_WIDTH - 1 : 0] out_packet_signals;
wire internal_out_ready;
wire internal_out_valid;
wire [ADDR_WIDTH : 0] out_fill_level;
reg [ADDR_WIDTH : 0] out_fifo_fill_level;
reg [ADDR_WIDTH : 0] in_fill_level;
reg [ADDR_WIDTH : 0] in_space_avail;
reg [23 : 0] almost_empty_threshold;
reg [23 : 0] almost_full_threshold;
reg sink_in_reset;
// --------------------------------------------------
// Define Payload
//
// Icky part where we decide which signals form the
// payload to the FIFO.
// --------------------------------------------------
generate
if (EMPTY_WIDTH > 0) begin
assign in_packet_signals = {in_startofpacket, in_endofpacket, in_empty};
assign {out_startofpacket, out_endofpacket, out_empty} = out_packet_signals;
end
else begin
assign in_packet_signals = {in_startofpacket, in_endofpacket};
assign {out_startofpacket, out_endofpacket} = out_packet_signals;
end
endgenerate
generate
if (USE_PACKETS) begin
if (ERROR_WIDTH > 0) begin
if (CHANNEL_WIDTH > 0) begin
assign in_payload = {in_packet_signals, in_data, in_error, in_channel};
assign {out_packet_signals, out_data, out_error, out_channel} = out_payload;
end
else begin
assign in_payload = {in_packet_signals, in_data, in_error};
assign {out_packet_signals, out_data, out_error} = out_payload;
end
end
else begin
if (CHANNEL_WIDTH > 0) begin
assign in_payload = {in_packet_signals, in_data, in_channel};
assign {out_packet_signals, out_data, out_channel} = out_payload;
end
else begin
assign in_payload = {in_packet_signals, in_data};
assign {out_packet_signals, out_data} = out_payload;
end
end
end
else begin
if (ERROR_WIDTH > 0) begin
if (CHANNEL_WIDTH > 0) begin
assign in_payload = {in_data, in_error, in_channel};
assign {out_data, out_error, out_channel} = out_payload;
end
else begin
assign in_payload = {in_data, in_error};
assign {out_data, out_error} = out_payload;
end
end
else begin
if (CHANNEL_WIDTH > 0) begin
assign in_payload = {in_data, in_channel};
assign {out_data, out_channel} = out_payload;
end
else begin
assign in_payload = in_data;
assign out_data = out_payload;
end
end
assign out_packet_signals = 'b0;
end
endgenerate
// ---------------------------------------------------------------------
// Memory
//
// Infers a simple dual clock memory with unregistered outputs
// ---------------------------------------------------------------------
always @(posedge in_clk) begin
if (in_valid && in_ready)
mem[mem_wr_ptr] <= in_payload;
end
always @(posedge out_clk) begin
internal_out_payload <= mem[mem_rd_ptr];
end
assign mem_rd_ptr = next_out_rd_ptr;
assign mem_wr_ptr = in_wr_ptr;
// ---------------------------------------------------------------------
// Pointer Management
//
// Increment our good old read and write pointers on their native
// clock domains.
// ---------------------------------------------------------------------
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
in_wr_ptr <= 0;
in_wr_ptr_lookahead <= 1;
end
else begin
in_wr_ptr <= next_in_wr_ptr;
in_wr_ptr_lookahead <= (in_valid && in_ready) ? in_wr_ptr_lookahead + 1'b1 : in_wr_ptr_lookahead;
end
end
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n) begin
out_rd_ptr <= 0;
out_rd_ptr_lookahead <= 1;
end
else begin
out_rd_ptr <= next_out_rd_ptr;
out_rd_ptr_lookahead <= (internal_out_valid && internal_out_ready) ? out_rd_ptr_lookahead + 1'b1 : out_rd_ptr_lookahead;
end
end
generate if (LOOKAHEAD_POINTERS) begin : lookahead_pointers
assign next_in_wr_ptr = (in_ready && in_valid) ? in_wr_ptr_lookahead : in_wr_ptr;
assign next_out_rd_ptr = (internal_out_ready && internal_out_valid) ? out_rd_ptr_lookahead : out_rd_ptr;
end
else begin : non_lookahead_pointers
assign next_in_wr_ptr = (in_ready && in_valid) ? in_wr_ptr + 1'b1 : in_wr_ptr;
assign next_out_rd_ptr = (internal_out_ready && internal_out_valid) ? out_rd_ptr + 1'b1 : out_rd_ptr;
end
endgenerate
// ---------------------------------------------------------------------
// Empty/Full Signal Generation
//
// We keep read and write pointers that are one bit wider than
// required, and use that additional bit to figure out if we're
// full or empty.
// ---------------------------------------------------------------------
always @(posedge out_clk or negedge out_reset_n) begin
if(!out_reset_n)
empty <= 1;
else
empty <= (next_out_rd_ptr == next_out_wr_ptr);
end
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
full <= 0;
sink_in_reset <= 1'b1;
end
else begin
full <= (next_in_rd_ptr[ADDR_WIDTH - 1 : 0] == next_in_wr_ptr[ADDR_WIDTH - 1 : 0]) && (next_in_rd_ptr[ADDR_WIDTH] != next_in_wr_ptr[ADDR_WIDTH]);
sink_in_reset <= 1'b0;
end
end
// ---------------------------------------------------------------------
// Write Pointer Clock Crossing
//
// Clock crossing is done with gray encoding of the pointers. What? You
// want to know more? We ensure a one bit change at sampling time,
// and then metastable harden the sampled gray pointer.
// ---------------------------------------------------------------------
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n)
in_wr_ptr_gray <= 0;
else
in_wr_ptr_gray <= bin2gray(in_wr_ptr);
end
altera_dcfifo_synchronizer_bundle #(.WIDTH(ADDR_WIDTH+1), .DEPTH(WR_SYNC_DEPTH))
write_crosser (
.clk(out_clk),
.reset_n(out_reset_n),
.din(in_wr_ptr_gray),
.dout(out_wr_ptr_gray)
);
// ---------------------------------------------------------------------
// Optionally pipeline the gray to binary conversion for the write pointer.
// Doing this will increase the latency of the FIFO, but increase fmax.
// ---------------------------------------------------------------------
generate if (PIPELINE_POINTERS) begin : wr_ptr_pipeline
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n)
out_wr_ptr_gray_reg <= 0;
else
out_wr_ptr_gray_reg <= gray2bin(out_wr_ptr_gray);
end
assign next_out_wr_ptr = out_wr_ptr_gray_reg;
end
else begin : no_wr_ptr_pipeline
assign next_out_wr_ptr = gray2bin(out_wr_ptr_gray);
end
endgenerate
// ---------------------------------------------------------------------
// Read Pointer Clock Crossing
//
// Go the other way, go the other way...
// ---------------------------------------------------------------------
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n)
out_rd_ptr_gray <= 0;
else
out_rd_ptr_gray <= bin2gray(out_rd_ptr);
end
altera_dcfifo_synchronizer_bundle #(.WIDTH(ADDR_WIDTH+1), .DEPTH(RD_SYNC_DEPTH))
read_crosser (
.clk(in_clk),
.reset_n(in_reset_n),
.din(out_rd_ptr_gray),
.dout(in_rd_ptr_gray)
);
// ---------------------------------------------------------------------
// Optionally pipeline the gray to binary conversion of the read pointer.
// Doing this will increase the pessimism of the FIFO, but increase fmax.
// ---------------------------------------------------------------------
generate if (PIPELINE_POINTERS) begin : rd_ptr_pipeline
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n)
in_rd_ptr_gray_reg <= 0;
else
in_rd_ptr_gray_reg <= gray2bin(in_rd_ptr_gray);
end
assign next_in_rd_ptr = in_rd_ptr_gray_reg;
end
else begin : no_rd_ptr_pipeline
assign next_in_rd_ptr = gray2bin(in_rd_ptr_gray);
end
endgenerate
// ---------------------------------------------------------------------
// Avalon ST Signals
// ---------------------------------------------------------------------
assign in_ready = BACKPRESSURE_DURING_RESET ? !(full || sink_in_reset) : !full;
assign internal_out_valid = !empty;
// --------------------------------------------------
// Output Pipeline Stage
//
// We do this on the single clock FIFO to keep fmax
// up because the memory outputs are kind of slow.
// Therefore, this stage is even more critical on a dual clock
// FIFO, wouldn't you say? No one wants a slow dcfifo.
// --------------------------------------------------
assign internal_out_ready = out_ready || !out_valid;
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n) begin
out_valid <= 0;
out_payload <= 0;
end
else begin
if (internal_out_ready) begin
out_valid <= internal_out_valid;
out_payload <= internal_out_payload;
end
end
end
// ---------------------------------------------------------------------
// Out Fill Level
//
// As in the SCFIFO, we account for the output stage as well in the
// fill level calculations. This means that the out fill level always
// gives the most accurate fill level report.
//
// On a full 16-deep FIFO, the out fill level will read 17. Funny, but
// accurate.
//
// That's essential on the output side, because a downstream component
// might want to know the exact amount of data in the FIFO at any time.
// ---------------------------------------------------------------------
generate
if (USE_OUT_FILL_LEVEL || STREAM_ALMOST_EMPTY) begin
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n) begin
out_fifo_fill_level <= 0;
end
else begin
out_fifo_fill_level <= next_out_wr_ptr - next_out_rd_ptr;
end
end
assign out_fill_level = out_fifo_fill_level + {{ADDR_WIDTH{1'b0}}, out_valid};
end
endgenerate
// ---------------------------------------------------------------------
// Almost Empty Streaming Status & Out CSR
//
// This is banal by now, but where's the empty signal? The output side.
// Where's the almost empty status? The output side.
//
// The almost empty signal is asserted when the output fill level
// in the FIFO falls below the user-specified threshold.
//
// Output CSR address map:
//
// | Addr | RW | 31 - 24 | 23 - 0 |
// | 0 | R | Reserved | Out fill level |
// | 1 | RW | Reserved | Almost empty threshold |
// ---------------------------------------------------------------------
generate
if (USE_OUT_FILL_LEVEL || STREAM_ALMOST_EMPTY) begin
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n) begin
out_csr_readdata <= 0;
if (STREAM_ALMOST_EMPTY)
almost_empty_threshold <= 0;
end
else begin
if (out_csr_write) begin
if (STREAM_ALMOST_EMPTY && (out_csr_address == 1))
almost_empty_threshold <= out_csr_writedata[23 : 0];
end
else if (out_csr_read) begin
out_csr_readdata <= 0;
if (out_csr_address == 0)
out_csr_readdata[23 : 0] <= out_fill_level;
else if (STREAM_ALMOST_EMPTY && (out_csr_address == 1))
out_csr_readdata[23 : 0] <= almost_empty_threshold;
end
end
end
end
if (STREAM_ALMOST_EMPTY) begin
always @(posedge out_clk or negedge out_reset_n) begin
if (!out_reset_n) begin
almost_empty_valid <= 0;
almost_empty_data <= 0;
end
else begin
almost_empty_valid <= 1'b1;
almost_empty_data <= (out_fill_level <= almost_empty_threshold);
end
end
end
endgenerate
// ---------------------------------------------------------------------
// In Fill Level & In Status Connection Point
//
// Note that the input fill level does not account for the output
// stage i.e it is only the fifo fill level.
//
// Is this a problem? No, because the input fill is usually used to
// see how much data can still be pushed into this FIFO. The FIFO
// fill level gives exactly this information, and there's no need to
// make our lives more difficult by including the output stage here.
//
// One might ask: why not just report a space available level on the
// input side? Well, I'd like to make this FIFO be as similar as possible
// to its single clock cousin, and that uses fill levels and
// fill thresholds with nary a mention of space available.
// ---------------------------------------------------------------------
generate
if (USE_IN_FILL_LEVEL || STREAM_ALMOST_FULL) begin
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
in_fill_level <= 0;
end
else begin
in_fill_level <= next_in_wr_ptr - next_in_rd_ptr;
end
end
end
endgenerate
generate
if (USE_SPACE_AVAIL_IF) begin
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
in_space_avail <= FIFO_DEPTH;
end
else begin
// -------------------------------------
// space = DEPTH-fill = DEPTH-(wr-rd) = DEPTH+rd-wr
// Conveniently, DEPTH requires the same number of bits
// as the pointers, e.g. a dcfifo with depth = 8
// requires 4-bit pointers.
//
// Adding 8 to a 4-bit pointer is simply negating the
// first bit... as is done below.
// -------------------------------------
in_space_avail <= {~next_in_rd_ptr[ADDR_WIDTH],
next_in_rd_ptr[ADDR_WIDTH-1:0]} -
next_in_wr_ptr;
end
end
assign space_avail_data = in_space_avail;
end
else begin : gen_blk13_else
assign space_avail_data = 'b0;
end
endgenerate
// ---------------------------------------------------------------------
// Almost Full Streaming Status & In CSR
//
// Where's the full signal? The input side.
// Where's the almost full status? The input side.
//
// The almost full data bit is asserted when the input fill level
// in the FIFO goes above the user-specified threshold.
//
// Input csr port address map:
//
// | Addr | RW | 31 - 24 | 23 - 0 |
// | 0 | R | Reserved | In fill level |
// | 1 | RW | Reserved | Almost full threshold |
// ---------------------------------------------------------------------
generate
if (USE_IN_FILL_LEVEL || STREAM_ALMOST_FULL) begin
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
in_csr_readdata <= 0;
if (STREAM_ALMOST_FULL)
almost_full_threshold <= 0;
end
else begin
if (in_csr_write) begin
if (STREAM_ALMOST_FULL && (in_csr_address == 1))
almost_full_threshold <= in_csr_writedata[23 : 0];
end
else if (in_csr_read) begin
in_csr_readdata <= 0;
if (in_csr_address == 0)
in_csr_readdata[23 : 0] <= in_fill_level;
else if (STREAM_ALMOST_FULL && (in_csr_address == 1))
in_csr_readdata[23 : 0] <= almost_full_threshold;
end
end
end
end
if (STREAM_ALMOST_FULL) begin
always @(posedge in_clk or negedge in_reset_n) begin
if (!in_reset_n) begin
almost_full_valid <= 0;
almost_full_data <= 0;
end
else begin
almost_full_valid <= 1'b1;
almost_full_data <= (in_fill_level >= almost_full_threshold);
end
end
end
endgenerate
// ---------------------------------------------------------------------
// Gray Functions
//
// These are real beasts when you look at them. But they'll be
// tested thoroughly.
// ---------------------------------------------------------------------
function [ADDR_WIDTH : 0] bin2gray;
input [ADDR_WIDTH : 0] bin_val;
integer i;
for (i = 0; i <= ADDR_WIDTH; i = i + 1)
begin
if (i == ADDR_WIDTH)
bin2gray[i] = bin_val[i];
else
bin2gray[i] = bin_val[i+1] ^ bin_val[i];
end
endfunction
function [ADDR_WIDTH : 0] gray2bin;
input [ADDR_WIDTH : 0] gray_val;
integer i;
integer j;
for (i = 0; i <= ADDR_WIDTH; i = i + 1) begin
gray2bin[i] = gray_val[i];
for (j = ADDR_WIDTH; j > i; j = j - 1) begin
gray2bin[i] = gray2bin[i] ^ gray_val[j];
end
end
endfunction
// --------------------------------------------------
// Calculates the log2ceil of the input value
// --------------------------------------------------
function integer log2ceil;
input integer val;
integer i;
begin
i = 1;
log2ceil = 0;
while (i < val) begin
log2ceil = log2ceil + 1;
i = i << 1;
end
end
endfunction
endmodule