// Copyright © 2019-2023 // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. `include "VX_platform.vh" `TRACING_OFF module VX_fifo_queue #( parameter DATAW = 1, parameter DEPTH = 2, parameter ALM_FULL = (DEPTH - 1), parameter ALM_EMPTY = 1, parameter OUT_REG = 0, parameter LUTRAM = 1, parameter SIZEW = `CLOG2(DEPTH+1) ) ( input wire clk, input wire reset, input wire push, input wire pop, input wire [DATAW-1:0] data_in, output wire [DATAW-1:0] data_out, output wire empty, output wire alm_empty, output wire full, output wire alm_full, output wire [SIZEW-1:0] size ); localparam ADDRW = `CLOG2(DEPTH); `STATIC_ASSERT(ALM_FULL > 0, ("alm_full must be greater than 0!")) `STATIC_ASSERT(ALM_FULL < DEPTH, ("alm_full must be smaller than size!")) `STATIC_ASSERT(ALM_EMPTY > 0, ("alm_empty must be greater than 0!")) `STATIC_ASSERT(ALM_EMPTY < DEPTH, ("alm_empty must be smaller than size!")) `STATIC_ASSERT(`ISPOW2(DEPTH), ("size must be a power of 2!")) if (DEPTH == 1) begin reg [DATAW-1:0] head_r; reg size_r; always @(posedge clk) begin if (reset) begin head_r <= '0; size_r <= '0; end else begin `ASSERT(~push || ~full, ("runtime error: writing to a full queue")); `ASSERT(~pop || ~empty, ("runtime error: reading an empty queue")); if (push) begin if (~pop) begin size_r <= 1; end end else if (pop) begin size_r <= '0; end if (push) begin head_r <= data_in; end end end assign data_out = head_r; assign empty = (size_r == 0); assign alm_empty = 1'b1; assign full = (size_r != 0); assign alm_full = 1'b1; assign size = size_r; end else begin reg empty_r, alm_empty_r; reg full_r, alm_full_r; reg [ADDRW-1:0] used_r; wire [ADDRW-1:0] used_n; always @(posedge clk) begin if (reset) begin empty_r <= 1; alm_empty_r <= 1; full_r <= 0; alm_full_r <= 0; used_r <= '0; end else begin `ASSERT(~(push && ~pop) || ~full, ("runtime error: incrementing full queue")); `ASSERT(~(pop && ~push) || ~empty, ("runtime error: decrementing empty queue")); if (push) begin if (~pop) begin empty_r <= 0; if (used_r == ADDRW'(ALM_EMPTY)) alm_empty_r <= 0; if (used_r == ADDRW'(DEPTH-1)) full_r <= 1; if (used_r == ADDRW'(ALM_FULL-1)) alm_full_r <= 1; end end else if (pop) begin full_r <= 0; if (used_r == ADDRW'(ALM_FULL)) alm_full_r <= 0; if (used_r == ADDRW'(1)) empty_r <= 1; if (used_r == ADDRW'(ALM_EMPTY+1)) alm_empty_r <= 1; end used_r <= used_n; end end if (DEPTH == 2) begin assign used_n = used_r ^ (push ^ pop); if (0 == OUT_REG) begin reg [1:0][DATAW-1:0] shift_reg; always @(posedge clk) begin if (push) begin shift_reg[1] <= shift_reg[0]; shift_reg[0] <= data_in; end end assign data_out = shift_reg[!used_r[0]]; end else begin reg [DATAW-1:0] data_out_r; reg [DATAW-1:0] buffer; always @(posedge clk) begin if (push) begin buffer <= data_in; end if (push && (empty_r || (used_r && pop))) begin data_out_r <= data_in; end else if (pop) begin data_out_r <= buffer; end end assign data_out = data_out_r; end end else begin assign used_n = $signed(used_r) + ADDRW'($signed(2'(push) - 2'(pop))); if (0 == OUT_REG) begin reg [ADDRW-1:0] rd_ptr_r; reg [ADDRW-1:0] wr_ptr_r; always @(posedge clk) begin if (reset) begin rd_ptr_r <= '0; wr_ptr_r <= '0; end else begin wr_ptr_r <= wr_ptr_r + ADDRW'(push); rd_ptr_r <= rd_ptr_r + ADDRW'(pop); end end VX_dp_ram #( .DATAW (DATAW), .SIZE (DEPTH), .LUTRAM (LUTRAM) ) dp_ram ( .clk(clk), .read (1'b1), .write (push), `UNUSED_PIN (wren), .waddr (wr_ptr_r), .wdata (data_in), .raddr (rd_ptr_r), .rdata (data_out) ); end else begin wire [DATAW-1:0] dout; reg [DATAW-1:0] dout_r; reg [ADDRW-1:0] wr_ptr_r; reg [ADDRW-1:0] rd_ptr_r; reg [ADDRW-1:0] rd_ptr_n_r; always @(posedge clk) begin if (reset) begin wr_ptr_r <= '0; rd_ptr_r <= '0; rd_ptr_n_r <= 1; end else begin wr_ptr_r <= wr_ptr_r + ADDRW'(push); if (pop) begin rd_ptr_r <= rd_ptr_n_r; if (DEPTH > 2) begin rd_ptr_n_r <= rd_ptr_r + ADDRW'(2); end else begin // (DEPTH == 2); rd_ptr_n_r <= ~rd_ptr_n_r; end end end end wire going_empty; if (ALM_EMPTY == 1) begin assign going_empty = alm_empty_r; end else begin assign going_empty = (used_r == ADDRW'(1)); end VX_dp_ram #( .DATAW (DATAW), .SIZE (DEPTH), .LUTRAM (LUTRAM) ) dp_ram ( .clk (clk), .read (1'b1), .write (push), `UNUSED_PIN (wren), .waddr (wr_ptr_r), .wdata (data_in), .raddr (rd_ptr_n_r), .rdata (dout) ); always @(posedge clk) begin if (push && (empty_r || (going_empty && pop))) begin dout_r <= data_in; end else if (pop) begin dout_r <= dout; end end assign data_out = dout_r; end end assign empty = empty_r; assign alm_empty = alm_empty_r; assign full = full_r; assign alm_full = alm_full_r; assign size = {full_r, used_r}; end endmodule `TRACING_ON