mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-19 03:44:46 -04:00
Multiple changes to clean up code and remove Spyglass warnings. Co-authored-by: JeanRochCoulon <jean-roch.coulon@thalesgroup.com>
231 lines
8.5 KiB
Systemverilog
231 lines
8.5 KiB
Systemverilog
// Copyright 2018 ETH Zurich and University of Bologna.
|
|
// Copyright 2024 - PlanV Technologies for additionnal contribution.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this 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.
|
|
|
|
// Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch>
|
|
// Additional contributions by:
|
|
// Angela Gonzalez - PlanV Technologies
|
|
|
|
module cva6_fifo_v3 #(
|
|
parameter bit FALL_THROUGH = 1'b0, // fifo is in fall-through mode
|
|
parameter bit FPGA_ALTERA = 1'b0, // FPGA Altera optimizations enabled
|
|
parameter int unsigned DATA_WIDTH = 32, // default data width if the fifo is of type logic
|
|
parameter int unsigned DEPTH = 8, // depth can be arbitrary from 0 to 2**32
|
|
parameter type dtype = logic [DATA_WIDTH-1:0],
|
|
parameter bit FPGA_EN = 1'b0,
|
|
// DO NOT OVERWRITE THIS PARAMETER
|
|
parameter int unsigned ADDR_DEPTH = (DEPTH > 1) ? $clog2(DEPTH) : 1
|
|
) (
|
|
input logic clk_i, // Clock
|
|
input logic rst_ni, // Asynchronous reset active low
|
|
input logic flush_i, // flush the queue
|
|
input logic testmode_i, // test_mode to bypass clock gating
|
|
// status flags
|
|
output logic full_o, // queue is full
|
|
output logic empty_o, // queue is empty
|
|
output logic [ADDR_DEPTH-1:0] usage_o, // fill pointer
|
|
// as long as the queue is not full we can push new data
|
|
input dtype data_i, // data to push into the queue
|
|
input logic push_i, // data is valid and can be pushed to the queue
|
|
// as long as the queue is not empty we can pop new elements
|
|
output dtype data_o, // output data
|
|
input logic pop_i // pop head from queue
|
|
);
|
|
// local parameter
|
|
// FIFO depth - handle the case of pass-through, synthesizer will do constant propagation
|
|
localparam int unsigned FifoDepth = (DEPTH > 0) ? DEPTH : 1;
|
|
// clock gating control
|
|
logic gate_clock;
|
|
// pointer to the read and write section of the queue
|
|
logic [ADDR_DEPTH - 1:0] read_pointer_n, read_pointer_q, write_pointer_n, write_pointer_q;
|
|
// keep a counter to keep track of the current queue status
|
|
// this integer will be truncated by the synthesis tool
|
|
logic [ADDR_DEPTH:0] status_cnt_n, status_cnt_q;
|
|
// actual memory
|
|
dtype [FifoDepth - 1:0] mem_n, mem_q;
|
|
dtype data_ft_n, data_ft_q;
|
|
logic first_word_n, first_word_q;
|
|
|
|
// fifo ram signals for fpga target
|
|
logic fifo_ram_we;
|
|
logic [ADDR_DEPTH-1:0] fifo_ram_read_address;
|
|
logic [ADDR_DEPTH-1:0] fifo_ram_write_address;
|
|
logic [$bits(dtype)-1:0] fifo_ram_wdata;
|
|
logic [$bits(dtype)-1:0] fifo_ram_rdata;
|
|
|
|
assign usage_o = status_cnt_q[ADDR_DEPTH-1:0];
|
|
|
|
if (DEPTH == 0) begin : gen_pass_through
|
|
assign empty_o = ~push_i;
|
|
assign full_o = ~pop_i;
|
|
end else begin : gen_fifo
|
|
assign full_o = (status_cnt_q == FifoDepth[ADDR_DEPTH:0]);
|
|
assign empty_o = (status_cnt_q == 0) & ~(FALL_THROUGH & push_i);
|
|
end
|
|
// status flags
|
|
|
|
// read and write queue logic
|
|
always_comb begin : read_write_comb
|
|
// default assignment
|
|
read_pointer_n = read_pointer_q;
|
|
write_pointer_n = write_pointer_q;
|
|
status_cnt_n = status_cnt_q;
|
|
if (FPGA_EN && FPGA_ALTERA) data_ft_n = data_ft_q;
|
|
if (FPGA_EN && FPGA_ALTERA) first_word_n = first_word_q;
|
|
if (FPGA_EN) begin
|
|
fifo_ram_we = '0;
|
|
fifo_ram_write_address = '0;
|
|
fifo_ram_wdata = '0;
|
|
if (DEPTH == 0) begin
|
|
data_o = data_i;
|
|
end else begin
|
|
if (FPGA_ALTERA) data_o = first_word_q ? data_ft_q : fifo_ram_rdata;
|
|
else data_o = fifo_ram_rdata;
|
|
end
|
|
end else begin
|
|
data_o = (DEPTH == 0) ? data_i : mem_q[read_pointer_q];
|
|
mem_n = mem_q;
|
|
gate_clock = 1'b1;
|
|
end
|
|
|
|
// push a new element to the queue
|
|
if (push_i && ~full_o) begin
|
|
if (FPGA_EN) begin
|
|
fifo_ram_we = 1'b1;
|
|
fifo_ram_write_address = write_pointer_q;
|
|
fifo_ram_wdata = data_i;
|
|
if (FPGA_ALTERA) first_word_n = first_word_q && pop_i;
|
|
end else begin
|
|
// push the data onto the queue
|
|
mem_n[write_pointer_q] = data_i;
|
|
// un-gate the clock, we want to write something
|
|
gate_clock = 1'b0;
|
|
end
|
|
|
|
// increment the write counter
|
|
if (write_pointer_q == FifoDepth[ADDR_DEPTH-1:0] - 1) write_pointer_n = '0;
|
|
else write_pointer_n = write_pointer_q + 1;
|
|
// increment the overall counter
|
|
status_cnt_n = status_cnt_q + 1;
|
|
end
|
|
|
|
if (pop_i && ~empty_o) begin
|
|
data_ft_n = data_i;
|
|
if (FPGA_EN && FPGA_ALTERA) first_word_n = first_word_q && push_i;
|
|
// read from the queue is a default assignment
|
|
// but increment the read pointer...
|
|
if (read_pointer_n == FifoDepth[ADDR_DEPTH-1:0] - 1) read_pointer_n = '0;
|
|
else read_pointer_n = read_pointer_q + 1;
|
|
// ... and decrement the overall count
|
|
status_cnt_n = status_cnt_q - 1;
|
|
end
|
|
|
|
// keep the count pointer stable if we push and pop at the same time
|
|
if (push_i && pop_i && ~full_o && ~empty_o) status_cnt_n = status_cnt_q;
|
|
|
|
// FIFO is in pass through mode -> do not change the pointers
|
|
if ((FALL_THROUGH || (FPGA_EN && FPGA_ALTERA)) && (status_cnt_q == 0) && push_i) begin
|
|
if (FALL_THROUGH) data_o = data_i;
|
|
if (FPGA_EN && FPGA_ALTERA) begin
|
|
data_ft_n = data_i;
|
|
first_word_n = '1;
|
|
end
|
|
if (pop_i) begin
|
|
first_word_n = '0;
|
|
status_cnt_n = status_cnt_q;
|
|
read_pointer_n = read_pointer_q;
|
|
write_pointer_n = write_pointer_q;
|
|
end
|
|
end
|
|
|
|
if (FPGA_EN) fifo_ram_read_address = (FPGA_ALTERA == 1) ? read_pointer_n : read_pointer_q;
|
|
else fifo_ram_read_address = '0;
|
|
|
|
end
|
|
|
|
// sequential process
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
read_pointer_q <= '0;
|
|
write_pointer_q <= '0;
|
|
status_cnt_q <= '0;
|
|
if (FPGA_ALTERA) first_word_q <= '0;
|
|
if (FPGA_ALTERA) data_ft_q <= '0;
|
|
end else begin
|
|
if (flush_i) begin
|
|
read_pointer_q <= '0;
|
|
write_pointer_q <= '0;
|
|
status_cnt_q <= '0;
|
|
if (FPGA_ALTERA) first_word_q <= '0;
|
|
if (FPGA_ALTERA) data_ft_q <= '0;
|
|
end else begin
|
|
read_pointer_q <= read_pointer_n;
|
|
write_pointer_q <= write_pointer_n;
|
|
status_cnt_q <= status_cnt_n;
|
|
if (FPGA_ALTERA) data_ft_q <= data_ft_n;
|
|
if (FPGA_ALTERA) first_word_q <= first_word_n;
|
|
end
|
|
end
|
|
end
|
|
|
|
if (FPGA_EN) begin : gen_fpga_queue
|
|
if (FPGA_ALTERA) begin
|
|
SyncDpRam_ind_r_w #(
|
|
.ADDR_WIDTH(ADDR_DEPTH),
|
|
.DATA_DEPTH(DEPTH),
|
|
.DATA_WIDTH($bits(dtype))
|
|
) fifo_ram (
|
|
.Clk_CI (clk_i),
|
|
.WrEn_SI (fifo_ram_we),
|
|
.RdAddr_DI(fifo_ram_read_address),
|
|
.WrAddr_DI(fifo_ram_write_address),
|
|
.WrData_DI(fifo_ram_wdata),
|
|
.RdData_DO(fifo_ram_rdata)
|
|
);
|
|
end else begin
|
|
AsyncDpRam #(
|
|
.ADDR_WIDTH(ADDR_DEPTH),
|
|
.DATA_DEPTH(DEPTH),
|
|
.DATA_WIDTH($bits(dtype))
|
|
) fifo_ram (
|
|
.Clk_CI (clk_i),
|
|
.WrEn_SI (fifo_ram_we),
|
|
.RdAddr_DI(fifo_ram_read_address),
|
|
.WrAddr_DI(fifo_ram_write_address),
|
|
.WrData_DI(fifo_ram_wdata),
|
|
.RdData_DO(fifo_ram_rdata)
|
|
);
|
|
end
|
|
end else begin : gen_asic_queue
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (~rst_ni) begin
|
|
mem_q <= '0;
|
|
end else if (!gate_clock) begin
|
|
mem_q <= mem_n;
|
|
end
|
|
end
|
|
end
|
|
|
|
// pragma translate_off
|
|
initial begin
|
|
assert (DEPTH > 0)
|
|
else $error("DEPTH must be greater than 0.");
|
|
end
|
|
|
|
full_write :
|
|
assert property (@(posedge clk_i) disable iff (~rst_ni) (full_o |-> ~push_i))
|
|
else $fatal(1, "Trying to push new data although the FIFO is full.");
|
|
|
|
empty_read :
|
|
assert property (@(posedge clk_i) disable iff (~rst_ni) (empty_o |-> ~pop_i))
|
|
else $fatal(1, "Trying to pop data although the FIFO is empty.");
|
|
// pragma translate_on
|
|
|
|
endmodule // fifo_v3
|