/* * drop_fifo.v * * Copyright (C) 2023-2025 Private Island Networks Inc. * Copyright (C) 2018-2021 Mind Chasers Inc. * * 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. * * function: FIFO that supports dropping data if not kept * * For more information, see https://privateisland.tech/dev/pi-doc * */ module drop_fifo( input rstn, input phy_up, // Use the associated PHY state to gate write activity into the DF input rx_clk, input tx_clk, input passthrough, // don't store / delay data, write through instead input enable, // enable the FIFO input keep, // this packet won't be dropped, start transferring it (writer must be sure FIFO won't run dry during writes) // input data input we_in, input [8:0] d_in, input wr_done, // keep should occur before wr_done is asserted. input rx_idle, // Use to clean up states and pointers input rx_error, // output data output reg we_out, // assert when data is available for writing to the next stage output reg [8:0] d_out, // debug output reg active ); // local parameters and includes localparam SZ_DPRAM = 16'd2048; // nets and registers reg [10:0] wr_ptr0, wr_ptr1; // wr_ptr0 is updated on each write; wr_ptr1 is updated at end of packet reg [10:0] rd_ptr; wire [8:0] d_out_internal; reg read_run, read_run_m1, read_run_m2; // read continues while read_run is set reg wr_done_m1, wr_done_m2, wr_done_m3; // CDC flag reg rx_idle_m1, rx_idle_m2; reg kept; wire fifo_empty; reg [15:0] df_bytes; /* * kept: assert for length of write duration after keep asserts (when enable is active) */ always @(posedge rx_clk, negedge rstn) if(!rstn) kept <= 1'b0; else if (read_run_m2) // CDC flag that is asserted for multiple clocks kept <= 1'b0; else if (phy_up && keep && enable) kept <= 1'b1; /* * wr_ptr0 logic */ always @(posedge rx_clk, negedge rstn) if(!rstn) wr_ptr0 <= 'd0; else if (phy_up && we_in) wr_ptr0 <= wr_ptr0 + 1'b1; // convert clock domain for rx_idle always @(posedge tx_clk, negedge rstn) if(!rstn) begin rx_idle_m1 <= 1'b0; rx_idle_m2 <= 1'b0; end else begin rx_idle_m1 <= rx_idle; rx_idle_m2 <= rx_idle_m1; end // convert clock domain for wr_done always @(posedge tx_clk, negedge rstn) if(!rstn) begin wr_done_m1 <= 1'b0; wr_done_m2 <= 1'b0; wr_done_m3 <= 1'b0; end else begin wr_done_m1 <= wr_done; wr_done_m2 <= wr_done_m1; wr_done_m3 <= wr_done_m2; end /* * wr_ptr1 logic * sync pointers at end of RX packet */ always @(posedge tx_clk, negedge rstn) if(!rstn) wr_ptr1 <= 'd0; else if (phy_up && wr_done_m3) wr_ptr1 <= wr_ptr0; // else if (rx_idle_m2 && !read_run) wr_ptr1 <= wr_ptr0; // final clean up /* * read_run logic * continues until the FIFO is empty */ always @(posedge tx_clk, negedge rstn) if(!rstn) read_run <= 1'b0; else if (phy_up && kept && wr_done_m3) read_run <= 1'b1; else if (fifo_empty) read_run <= 1'b0; always @(posedge tx_clk, negedge rstn) if(!rstn) begin read_run_m1 <= 1'b0; read_run_m2 <= 1'b0; end else begin read_run_m1 <= read_run; read_run_m2 <= read_run_m1; end /* * rd_ptr logic */ always @(posedge tx_clk, negedge rstn) if(!rstn) rd_ptr <= 'd0; else if (phy_up && kept && wr_done_m2) rd_ptr <= wr_ptr1; // set rd_ptr to the beginning of where the write started else if (read_run && !fifo_empty) rd_ptr <= rd_ptr+ 1'b1; /* * we_out logic */ always @(posedge tx_clk, negedge rstn) if(!rstn) we_out <= 1'b0; else if (read_run && read_run_m1) we_out <= 1'b1; else we_out <= 1'b0; assign fifo_empty = (wr_ptr1 == rd_ptr); /* * d_out register * */ always @(posedge tx_clk, negedge rstn) if(!rstn) d_out <= 'd0; else d_out <= d_out_internal; // debug flag always @(posedge tx_clk, negedge rstn) if(!rstn) active <= 1'b0; else if (we_in || we_out) active <= 1'b1; else active <= 1'b0; /* * debug: bytes in DF */ always @(posedge rx_clk, negedge rstn) if(!rstn) df_bytes <= 'd0; else if (! phy_up) df_bytes <= 'd0; else if (wr_ptr1 <= wr_ptr0) df_bytes <= (wr_ptr0 + 1'b1) - wr_ptr1; else df_bytes <= SZ_DPRAM + (wr_ptr0+1'b1) - wr_ptr1; dpram_inf dpram_fifo( .rstn(rstn), .a_clk(rx_clk), .a_clk_e(1'b1), .a_we(we_in), .a_oe(1'b0), .a_addr(wr_ptr0), .a_din(d_in), .a_dout(), // port B .b_clk(tx_clk), .b_clk_e(read_run), .b_we(1'b0), .b_oe(1'b1), .b_addr(rd_ptr), .b_din(9'h0), .b_dout(d_out_internal) ); endmodule