/* * half_fifo.v * * Copyright (C) 2024-2025 Private Island Networks Inc. * Copyright (C) 2018-2022 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. * * Internal Controller / FIFO interface for accessing Ethernet Switch * * */ module half_fifo #(parameter DATA_WIDTH = 9, DPRAM_DEPTH=11) ( input rstn, input fifo_clk, input uc_clk, // controller interrupt support output reg rx_fifo_int, // signals that a complete packet has arrived input rx_fifo_int_acked, // controller interface input [DPRAM_DEPTH-1:0] dpram_addr, input [DPRAM_DEPTH-1:0] dpram_din, // size it to include 11-bit pointers output reg [DPRAM_DEPTH-1:0] dpram_dout, // size it to include 11-bit pointers input dpram_we, input dpram_oe, input dpram_ptrs_sel, input dpram_tx_sel, input dpram_rx_sel, // FIFO RX (switch -> hfifo -> controller) input fifo_we, input [DATA_WIDTH-1:0] fifo_d_in, // FIFO TX (controller -> hfifo -> switch) input fifo_re, output reg [10:0] tx_byte_cnt, output [DATA_WIDTH-1:0] fifo_d_out, output tx_empty ); `define INCLUDED `include "cont_params.v" `undef INCLUDED /* * Pointers for accessing memory. * UC writes TX and reads RX * Switch writes (via FIFO I/F) RX and reads TX * This is the opposite of the system view */ reg [DPRAM_DEPTH-1:0] rx_wr_ptr, rx_wr_ptr_latched; reg [DPRAM_DEPTH-1:0] tx_wr_ptr, tx_wr_ptr_latched; reg [DPRAM_DEPTH-1:0] tx_rd_ptr; reg tx_wr_ptr_written, tx_wr_ptr_written_m1, tx_wr_ptr_written_m2; reg fifo_we_m1; reg reset_ptrs; wire [DATA_WIDTH-1:0] dpram_tx_dout, dpram_rx_dout; /* read data mux, refer to addr params above */ always @(*) casez({ dpram_tx_sel, dpram_rx_sel, dpram_ptrs_sel, dpram_addr[2:0] }) 6'b001000: dpram_dout = tx_wr_ptr; 6'b001001: dpram_dout = tx_rd_ptr; // meta stable 6'b001010: dpram_dout = rx_wr_ptr_latched; 6'b001011: dpram_dout = 11'd0; 6'b001100: dpram_dout = {10'd0, reset_ptrs}; 6'b010???: dpram_dout = {2'b00, dpram_rx_dout}; 6'b100???: dpram_dout = {2'b00, dpram_tx_dout}; default: dpram_dout = {2'b00, dpram_rx_dout}; endcase always @(posedge uc_clk, negedge rstn) if (!rstn) reset_ptrs <= 1'b0; else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_RESET_PTRS) reset_ptrs <= dpram_din[0]; always @(posedge uc_clk, negedge rstn) if (!rstn) tx_byte_cnt <= 11'd0; else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_BYTE_CNT_ADDR) tx_byte_cnt <= dpram_din; /* TX wr ptr (UC writes) */ always @(posedge uc_clk, negedge rstn) if (!rstn) begin tx_wr_ptr <= 'h0; tx_wr_ptr_written <= 1'b0; end else if (reset_ptrs) begin tx_wr_ptr <= 'h0; tx_wr_ptr_written <= 1'b1; end else if (dpram_ptrs_sel && dpram_we && dpram_addr[2:0] == HF_TX_WR_PTR_ADDR) begin tx_wr_ptr <= dpram_din; tx_wr_ptr_written <= 1'b1; end else tx_wr_ptr_written <= 1'b0; always @(posedge fifo_clk, negedge rstn) if (!rstn) begin tx_wr_ptr_written_m1 <= 1'b0; tx_wr_ptr_written_m2 <= 1'b0; end else begin tx_wr_ptr_written_m1 <= tx_wr_ptr_written; tx_wr_ptr_written_m2 <= tx_wr_ptr_written_m1; end always @(posedge fifo_clk, negedge rstn) if(!rstn) tx_wr_ptr_latched <= 8'h00; else if (tx_wr_ptr_written_m2) tx_wr_ptr_latched <= tx_wr_ptr; /* TX rd ptr (switch reads via FIFO), auto increment */ always @(posedge fifo_clk, negedge rstn) if(!rstn) tx_rd_ptr <= 11'd0; else if (reset_ptrs) tx_rd_ptr <= 11'd0; else if (fifo_re && !tx_empty) // advance the pointer if FIFO isn't empty tx_rd_ptr <= tx_rd_ptr + 1'b1; /* RX wr ptr (switch writes via FIFO) */ always @(posedge fifo_clk, negedge rstn) if(!rstn) rx_wr_ptr <= 11'd0; else if (reset_ptrs) rx_wr_ptr <= 11'd0; else if (fifo_we) rx_wr_ptr <= rx_wr_ptr + 1'b1; assign tx_empty = (tx_rd_ptr == tx_wr_ptr_latched); /* Assert interrupt whenever fifo_we is negated (writing is done) */ always @(posedge fifo_clk, negedge rstn) if (!rstn) fifo_we_m1 <= 1'b0; else fifo_we_m1 <= fifo_we; // Metastability note: rx_wr_ptr_latched should be read one or more clock cycles after rx_fifo_int asserts always @(posedge fifo_clk, negedge rstn) if (!rstn) begin rx_fifo_int <= 1'b0; rx_wr_ptr_latched <= 11'd0; end else if (!fifo_we & fifo_we_m1) begin // end of write rx_fifo_int <= 1'b1; rx_wr_ptr_latched <= rx_wr_ptr; end else if (rx_fifo_int_acked) // clear interrupt rx_fifo_int <= 1'b0; /* controller uses A side, FIFO uses B side * controller writes TX path and reads RX path */ // 2K DPRAM dpram_inf dpram_tx( .rstn(rstn), .a_clk(uc_clk), .a_clk_e(dpram_tx_sel), .a_we(dpram_we), .a_oe(1'b1), .a_addr(dpram_addr), .a_din(dpram_din[8:0]), .a_dout(dpram_tx_dout), // port B .b_clk(fifo_clk), .b_clk_e(1'b1), .b_we(1'b0), .b_oe(fifo_re), .b_addr(tx_rd_ptr), .b_din(9'h0), .b_dout(fifo_d_out) ); // 2K DPRAM dpram_inf dpram_rx( .rstn(rstn), .a_clk(uc_clk), .a_clk_e(dpram_rx_sel), .a_we(1'b0), .a_oe(1'b1), .a_addr(dpram_addr), .a_din(9'h000), .a_dout(dpram_rx_dout), // port B .b_clk(fifo_clk), .b_clk_e(1'b1), .b_we(fifo_we), .b_oe(1'b0), .b_addr(rx_wr_ptr), .b_din(fifo_d_in), .b_dout() ); endmodule