// SPDX-License-Identifier: CERN-OHL-S-2.0
/*

Copyright (c) 2026 FPGA Ninja, LLC

Authors:
- Alex Forencich

*/

`resetall
`timescale 1ns / 1ps
`default_nettype none

/*
 * True dual-port, dual-clock RAM
 */
module taxi_ram_2rw_2c #
(
    parameter ADDR_W = 16,
    parameter DATA_W = 16,
    parameter logic STRB_EN = 1'b1,
    parameter STRB_W = DATA_W/8
)
(
    input  wire logic               a_clk,
    input  wire logic               a_en,
    input  wire logic [ADDR_W-1:0]  a_addr,
    input  wire logic               a_wr_en,
    input  wire logic [DATA_W-1:0]  a_wr_data,
    input  wire logic [STRB_W-1:0]  a_wr_strb = '1,
    output wire logic [DATA_W-1:0]  a_rd_data,

    input  wire logic               b_clk,
    input  wire logic               b_en,
    input  wire logic [ADDR_W-1:0]  b_addr,
    input  wire logic               b_wr_en,
    input  wire logic [DATA_W-1:0]  b_wr_data,
    input  wire logic [STRB_W-1:0]  b_wr_strb = '1,
    output wire logic [DATA_W-1:0]  b_rd_data
);

localparam BYTE_LANES = STRB_W;
localparam BYTE_W = DATA_W/BYTE_LANES;

// check configuration
if (STRB_EN && BYTE_W * STRB_W != DATA_W)
    $fatal(0, "Error: Data width not evenly divisible (instance %m)");

reg [DATA_W-1:0] a_rd_data_reg = '0;
reg [DATA_W-1:0] b_rd_data_reg = '0;

assign a_rd_data = a_rd_data_reg;
assign b_rd_data = b_rd_data_reg;

// verilator lint_off MULTIDRIVEN
// (* RAM_STYLE="BLOCK" *)
logic [DATA_W-1:0] mem[2**ADDR_W] = '{default: '0};
// verilator lint_on MULTIDRIVEN

always_ff @(posedge a_clk) begin
    if (a_en) begin
        if (a_wr_en) begin
            if (STRB_EN) begin
                for (integer i = 0; i < BYTE_LANES; i = i + 1) begin
                    if (a_wr_strb[i]) begin
                        mem[a_addr][BYTE_W*i +: BYTE_W] <= a_wr_data[BYTE_W*i +: BYTE_W];
                    end
                end
            end else begin
                mem[a_addr] <= a_wr_data;
            end
        end else begin
            a_rd_data_reg <= mem[a_addr];
        end
    end
end

always_ff @(posedge b_clk) begin
    if (b_en) begin
        if (b_wr_en) begin
            if (STRB_EN) begin
                for (integer i = 0; i < BYTE_LANES; i = i + 1) begin
                    if (b_wr_strb[i]) begin
                        mem[b_addr][BYTE_W*i +: BYTE_W] <= b_wr_data[BYTE_W*i +: BYTE_W];
                    end
                end
            end else begin
                mem[b_addr] <= b_wr_data;
            end
        end else begin
            b_rd_data_reg <= mem[b_addr];
        end
    end
end

endmodule

`resetall
