// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

/**
 * Dual-port RAM with 1 cycle read/write delay, 32 bit words.
 * Optionally an extra delay can be added to the B-side requests.
 * This can be used to mimick an asymmetric memory delay like having the stack
 * and heap live in fast SRAM and the intructions live in slow, non-volatile
 * memory.
 */

`include "prim_assert.sv"

module ram_2p #(
    parameter int Depth       = 128,
    parameter int BExtraDelay = 0,
    parameter     MemInitFile = ""
) (
    input               clk_i,
    input               rst_ni,

    input               a_req_i,
    input               a_we_i,
    input        [ 3:0] a_be_i,
    input        [31:0] a_addr_i,
    input        [31:0] a_wdata_i,
    output logic        a_rvalid_o,
    output logic [31:0] a_rdata_o,

    input               b_req_i,
    input               b_we_i,
    input        [ 3:0] b_be_i,
    input        [31:0] b_addr_i,
    input        [31:0] b_wdata_i,
    output logic        b_rvalid_o,
    output logic [31:0] b_rdata_o
);

  localparam int Aw = $clog2(Depth);

  logic [Aw-1:0] a_addr_idx;
  assign a_addr_idx = a_addr_i[Aw-1+2:2];
  logic [31-Aw:0] unused_a_addr_parts;
  assign unused_a_addr_parts = {a_addr_i[31:Aw+2], a_addr_i[1:0]};

  logic [Aw-1:0] b_addr_idx;
  assign b_addr_idx = b_addr_i[Aw-1+2:2];
  logic [31-Aw:0] unused_b_addr_parts;
  assign unused_b_addr_parts = {b_addr_i[31:Aw+2], b_addr_i[1:0]};

  // Convert byte mask to SRAM bit mask.
  logic [31:0] a_wmask;
  logic [31:0] b_wmask;
  always_comb begin
    for (int i = 0 ; i < 4 ; i++) begin
      // mask for read data
      a_wmask[8*i+:8] = {8{a_be_i[i]}};
      b_wmask[8*i+:8] = {8{b_be_i[i]}};
    end
  end

  logic        b_rvalid_d;
  logic        b_rvalid_q[(BExtraDelay==0) ? 1 : BExtraDelay];
  logic [31:0] b_rdata_d;
  logic [31:0] b_rdata_q[(BExtraDelay==0) ? 1 : BExtraDelay];

  always_ff @(posedge clk_i or negedge rst_ni) begin
    if (!rst_ni) begin
      a_rvalid_o <= '0;
      b_rvalid_o <= '0;
      for (integer i = 0; i < BExtraDelay; i=i+1) begin
        b_rvalid_q[i] <= '0;
        b_rdata_q[i] <= '0;
      end
    end else begin
      a_rvalid_o <= a_req_i;
      b_rvalid_o <= b_rvalid_d;
      b_rvalid_q[0] <= b_req_i;
      b_rdata_q[0] <= b_rdata_d;
      for (integer i = BExtraDelay-1; i > 0; i=i-1) begin
        b_rvalid_q[i] <= b_rvalid_q[i-1];
        b_rdata_q[i] <= b_rdata_q[i-1];
      end
    end
  end

  assign b_rvalid_d = (BExtraDelay == 0) ? b_req_i : b_rvalid_q[BExtraDelay-1];
  assign b_rdata_o = (BExtraDelay == 0) ? b_rdata_d : b_rdata_q[BExtraDelay-1];

  prim_ram_2p #(
    .Width(32),
    .Depth(Depth),
    .DataBitsPerMask(8),
    .MemInitFile(MemInitFile)
  ) u_ram (
    .clk_a_i   (clk_i),
    .clk_b_i   (clk_i),
    .a_req_i   (a_req_i),
    .a_write_i (a_we_i),
    .a_addr_i  (a_addr_idx),
    .a_wdata_i (a_wdata_i),
    .a_wmask_i (a_wmask),
    .a_rdata_o (a_rdata_o),
    .b_req_i   (b_req_i),
    .b_write_i (b_we_i),
    .b_addr_i  (b_addr_idx),
    .b_wdata_i (b_wdata_i),
    .b_wmask_i (b_wmask),
    .b_rdata_o (b_rdata_d),
    .cfg_i     ('0),
    .cfg_o     ()
  );

endmodule
