// FILE         : fifo_async.sv
// LICENSE      : Personal/academic use only. Commercial use requires a Commercial License.
// SOURCE       : Converted from fifo_async.vhd
// DATE         : 2026-06-16
// DESCRIPTION  : Asynchronous FIFO — depth 4 (DEPTHEXP=2), parameterized width.
//                Gray-code pointers; 2-FF synchronizers on each clock domain.
//                Active-LOW enables (wen_n, ren_n) and active-LOW resets.
//-----------------------------------------------------------------------------
`default_nettype none

module fifo_async #(
    parameter int unsigned WIDTH_G   = 8,   // data width
    parameter int unsigned DEPTHEXP  = 2    // depth = 2**DEPTHEXP = 4
)(
    // Write domain
    input  logic                  wrst_n,   // async active-LOW write reset
    input  logic                  wclk,     // write clock
    input  logic                  wen_n,    // active-LOW write enable
    input  logic [WIDTH_G-1:0]    wdata,    // write data
    output logic                  wfull,    // FIFO full (write domain)
    // Read domain
    input  logic                  rrst_n,   // async active-LOW read reset
    input  logic                  rclk,     // read clock
    input  logic                  ren_n,    // active-LOW read enable
    output logic [WIDTH_G-1:0]    rdata,    // read data
    output logic                  rempty    // FIFO empty (read domain)
);

  localparam int unsigned DEPTH   = 2**DEPTHEXP;
  localparam int unsigned PTR_W   = DEPTHEXP + 1;  // one extra bit for full/empty

  // ── FIFO memory ──────────────────────────────────────────────────────────
  logic [WIDTH_G-1:0] fifomem [0:DEPTH-1];

  // ── Pointer registers (Gray-coded, one domain each) ──────────────────────
  logic [PTR_W-1:0] waddr_wrside;    // write gray ptr (write domain)
  logic [PTR_W-1:0] raddr_rdside;    // read  gray ptr (read  domain)

  // ── 2-FF synchronizers ───────────────────────────────────────────────────
  logic [PTR_W-1:0] waddr_rdside0;   // stage-1 sync of write ptr → read domain
  logic [PTR_W-1:0] waddr_rdside;    // stage-2 sync of write ptr → read domain
  logic [PTR_W-1:0] raddr_wrside0;   // stage-1 sync of read  ptr → write domain
  logic [PTR_W-1:0] raddr_wrside;    // stage-2 sync of read  ptr → write domain

  // ── Binary counts (decoded from gray) ────────────────────────────────────
  logic [PTR_W-1:0] wrbincount_wrside;  // write binary count (write domain)
  logic [PTR_W-1:0] rdbincount_rdside;  // read  binary count (read  domain)
  logic [PTR_W-1:0] rdbincount_wrside;  // read  binary count (write domain)

  // ── Internal enable signals (active-HIGH) ────────────────────────────────
  logic we;   // write enable active-HIGH
  logic re;   // read  enable active-HIGH

  assign we = ~wen_n;
  assign re = ~ren_n;

  // ── Gray-to-binary decode function ───────────────────────────────────────
  function automatic logic [PTR_W-1:0] gray2bin (input logic [PTR_W-1:0] gray);
    logic [PTR_W-1:0] bin;
    for (int i = 0; i < PTR_W; i++)
      bin[i] = ^(gray >> i);
    return bin;
  endfunction

  // ── Binary-to-gray encode function ───────────────────────────────────────
  function automatic logic [PTR_W-1:0] bin2gray (input logic [PTR_W-1:0] bin);
    return bin ^ (bin >> 1);
  endfunction

  // ── Write pointer counter (write domain) ─────────────────────────────────
  always_ff @(posedge wclk or negedge wrst_n) begin : g_wptr
    if (!wrst_n) begin
      wrbincount_wrside <= '0;
      waddr_wrside      <= '0;
    end else if (we && !wfull) begin
      wrbincount_wrside <= wrbincount_wrside + PTR_W'(1);
      waddr_wrside      <= bin2gray(wrbincount_wrside + PTR_W'(1));
    end
  end

  // ── Read pointer counter (read domain) ───────────────────────────────────
  always_ff @(posedge rclk or negedge rrst_n) begin : g_rptr
    if (!rrst_n) begin
      rdbincount_rdside <= '0;
      raddr_rdside      <= '0;
    end else if (re && !rempty) begin
      rdbincount_rdside <= rdbincount_rdside + PTR_W'(1);
      raddr_rdside      <= bin2gray(rdbincount_rdside + PTR_W'(1));
    end
  end

  // ── 2-FF sync: write ptr → read domain ───────────────────────────────────
  always_ff @(posedge rclk or negedge rrst_n) begin : g_wptr_sync
    if (!rrst_n) begin
      waddr_rdside0 <= '0;
      waddr_rdside  <= '0;
    end else begin
      waddr_rdside0 <= waddr_wrside;
      waddr_rdside  <= waddr_rdside0;
    end
  end

  // ── 2-FF sync: read ptr → write domain ───────────────────────────────────
  always_ff @(posedge wclk or negedge wrst_n) begin : g_rptr_sync
    if (!wrst_n) begin
      raddr_wrside0 <= '0;
      raddr_wrside  <= '0;
    end else begin
      raddr_wrside0 <= raddr_rdside;
      raddr_wrside  <= raddr_wrside0;
    end
  end

  // ── FIFO memory write ─────────────────────────────────────────────────────
  always_ff @(posedge wclk) begin : g_mem_wr
    if (we && !wfull)
      fifomem[wrbincount_wrside[DEPTHEXP-1:0]] <= wdata;
  end

  // ── Read data (combinational) ─────────────────────────────────────────────
  assign rdata = fifomem[rdbincount_rdside[DEPTHEXP-1:0]];

  // ── Empty flag (read domain) ──────────────────────────────────────────────
  assign rempty = (raddr_rdside == waddr_rdside);

  // ── Full flag (write domain) ──────────────────────────────────────────────
  assign rdbincount_wrside = gray2bin(raddr_wrside);
  assign wfull = (rdbincount_wrside[DEPTHEXP-1:0] == wrbincount_wrside[DEPTHEXP-1:0]) &&
                 (rdbincount_wrside[DEPTHEXP]      != wrbincount_wrside[DEPTHEXP]);

endmodule : fifo_async

`default_nettype wire