/* * drop2_fifo.v * * Copyright (C) 2018, 2019 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: Double buffered side-by-side FIFO * Uses 2 DPRAMs with additional logic for paths involving 1G to 100Mbit * */ `timescale 1ns /10ps module drop2_fifo( input rstn, input clk, input enable, // control (drop, keep, and done are asserted for one clock ) input keep, // this packet won't be dropped, start transferring it (KEEPS ARE AND'ED) input passthrough, // don't store data, write through instead // input input we_in, input wr_done, input [8:0] d_in, // output output we_out, output reg [8:0] d_out, // debug output reg active ); // local parameters and includes // nets and registers // the msbit of the ptr selects the DPRAM reg [11:0] wr_ptr; reg [11:0] rd_ptr; wire [8:0] d_out_0, d_out_1, d_out_internal; reg read_run, read_run_m1; // read continues while read_run is set reg i_we_out, i_we_out_m1; // internal we_out wire re; // read enable for dpram reg rd_done_p1, rd_done; wire i_keep; reg kept; assign i_keep = keep & enable; wire dpram0_a_clk_e, dpram1_a_clk_e; wire dpram0_b_clk_e, dpram1_b_clk_e; /* * kept: assert for length of write duration after keep asserts */ always @(posedge clk, negedge rstn) if( !rstn ) kept <= 1'b0; else if ( wr_done ) kept <= 1'b0; else if ( i_keep ) kept <= 1'b1; /* * read_run logic * read starts after wr_done * continues until the FIFO is empty */ always @(posedge clk, negedge rstn) if( !rstn ) begin read_run <= 1'b0; read_run_m1 <= 1'b0; end else if ( rd_done_p1 ) begin read_run <= 1'b0; read_run_m1 <= 1'b0; end else if ( kept && wr_done ) begin read_run <= 1'b1; end else read_run_m1 <= read_run; assign re = read_run; /* * we_out logic * delayed version of re */ always @(posedge clk, negedge rstn) if( !rstn ) i_we_out <= 1'b0; else if ( read_run && read_run_m1 ) i_we_out <= 1'b1; else i_we_out <= 1'b0; always @(posedge clk, negedge rstn) if( !rstn ) i_we_out_m1 <= 1'b0; else i_we_out_m1 <= i_we_out; // OR these two signals to stretch we_out to the last byte from FIFO assign we_out = i_we_out | i_we_out_m1; /* * upper / lower half DPRAM logic * directs upper or lower write buffer in FIFO * toggle on each done event (rx_data is complete) * don't toggle on a drop event (reuse buffer) */ always @(posedge clk, negedge rstn) if ( !rstn ) wr_ptr[11] <= 1'b0; // init to the lower bank else if ( wr_done && kept ) wr_ptr[11] <= ~wr_ptr[11]; /* * wr_ptr logic excluding msbit * reset pointer when wr_done */ always @(posedge clk, negedge rstn) if( !rstn ) wr_ptr[10:0] <= 'd0; else if ( wr_done ) wr_ptr[10:0] <= 'd0; else if ( we_in ) wr_ptr[10:0] <= wr_ptr[10:0] + 1; /* * each time the FIFO is empty, set rd_ptr[11] to wr_ptr[11] * FIFO becomes empty through reading */ always @(posedge clk, negedge rstn) if ( !rstn ) rd_ptr[11] <= 1'b0; // init to the lower bank else if ( rd_done ) rd_ptr[11] <= wr_ptr[11]; /* * rd_ptr logic excluding msbit */ always @(posedge clk, negedge rstn) if( !rstn ) rd_ptr[10:0] <= 'd0; else if ( rd_done ) rd_ptr[10:0] <= 'd0; else if ( re ) rd_ptr[10:0] <= rd_ptr[10:0] + 1; /* * d_out register * */ always @(posedge clk, negedge rstn) if( !rstn ) d_out <= 'd0; else d_out <= d_out_internal; /* * rd_done logic */ always @(posedge clk, negedge rstn) if( !rstn ) begin rd_done_p1 <= 1'b0; rd_done <= 1'b0; end else begin rd_done_p1 <= d_out_internal[8]; rd_done <= rd_done_p1; end // debug always @(posedge clk, negedge rstn) if( !rstn ) active <= 1'b0; else if ( we_in || we_out ) active <= 1'b1; else active <= 1'b0; assign dpram0_a_clk_e = ~wr_ptr[11]; assign dpram1_a_clk_e = wr_ptr[11]; assign dpram0_b_clk_e = ~rd_ptr[11]; assign dpram1_b_clk_e = rd_ptr[11]; assign d_out_internal = dpram0_b_clk_e ? d_out_0 : d_out_1; dpram dpram_0( .rstn( rstn ), .a_clk( clk ), .a_clk_e( dpram0_a_clk_e ), .a_we( we_in ), .a_oe( 1'b0 ), .a_addr( wr_ptr[10:0] ), .a_din( d_in ), .a_dout( ), // port B .b_clk( clk ), .b_clk_e( dpram0_b_clk_e ), .b_we( 1'b0 ), .b_oe( re ), .b_addr( rd_ptr[10:0] ), .b_din( 9'h0 ), .b_dout( d_out_0 ) ); dpram dpram_1( .rstn( rstn ), .a_clk( clk ), .a_clk_e( dpram1_a_clk_e ), .a_we( we_in ), .a_oe( 1'b0 ), .a_addr( wr_ptr[10:0] ), .a_din( d_in ), .a_dout( ), // port B .b_clk( clk ), .b_clk_e( dpram1_b_clk_e ), .b_we( 1'b0 ), .b_oe( re ), .b_addr( rd_ptr[10:0] ), .b_din( 9'h0 ), .b_dout( d_out_1 ) ); endmodule