// Copyright 2017 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the “License”); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.

////////////////////////////////////////////////////////////////////////////////
// Engineer:       Antonio Pullini - pullinia@iis.ee.ethz.ch                  //
//                                                                            //
// Additional contributions by:                                               //
//                 Sven Stucki - svstucki@student.ethz.ch                     //
//                 Markus Wegmann - markus.wegmann@technokrat.ch              //
//                                                                            //
// Design Name:    RISC-V register file                                       //
// Project Name:   zero-riscy                                                 //
// Language:       SystemVerilog                                              //
//                                                                            //
// Description:    Register file with 31 or 15x 32 bit wide registers.        //
//                 Register 0 is fixed to 0. This register file is based on   //
//                 latches and is thus smaller than the flip-flop based RF.   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////

`include "zeroriscy_config.sv"

module zeroriscy_register_file
#(
  parameter RV32E         = 0,
  parameter DATA_WIDTH    = 32
)
(
  // Clock and Reset
  input  logic                   clk,
  input  logic                   rst_n,

  input  logic                   test_en_i,

  //Read port R1
  input  logic [4:0]             raddr_a_i,
  output logic [DATA_WIDTH-1:0]  rdata_a_o,

  //Read port R2
  input  logic [4:0]             raddr_b_i,
  output logic [DATA_WIDTH-1:0]  rdata_b_o,


  // Write port W1
  input  logic [4:0]              waddr_a_i,
  input  logic [DATA_WIDTH-1:0]   wdata_a_i,
  input  logic                    we_a_i

);


  localparam    ADDR_WIDTH = RV32E ? 4 : 5;
  localparam    NUM_WORDS  = 2**ADDR_WIDTH;

  logic [DATA_WIDTH-1:0]      mem[NUM_WORDS];

  logic [NUM_WORDS-1:1]       waddr_onehot_a;

  logic [NUM_WORDS-1:1]       mem_clocks;
  logic [DATA_WIDTH-1:0]      wdata_a_q;


  // Write port W1
  logic [ADDR_WIDTH-1:0]     raddr_a_int, raddr_b_int, waddr_a_int;

  assign raddr_a_int = raddr_a_i[ADDR_WIDTH-1:0];
  assign raddr_b_int = raddr_b_i[ADDR_WIDTH-1:0];
  assign waddr_a_int = waddr_a_i[ADDR_WIDTH-1:0];


  logic clk_int;

  int unsigned i;
  int unsigned j;
  int unsigned k;

  genvar x;

  //-----------------------------------------------------------------------------
  //-- READ : Read address decoder RAD
  //-----------------------------------------------------------------------------
  assign rdata_a_o = mem[raddr_a_int];
  assign rdata_b_o = mem[raddr_b_int];

  //-----------------------------------------------------------------------------
  // WRITE : SAMPLE INPUT DATA
  //---------------------------------------------------------------------------

  cluster_clock_gating CG_WE_GLOBAL
  (
    .clk_i     ( clk             ),
    .en_i      ( we_a_i          ),
    .test_en_i ( test_en_i       ),
    .clk_o     ( clk_int         )
  );

  // use clk_int here, since otherwise we don't want to write anything anyway
  always_ff @(posedge clk_int, negedge rst_n)
  begin : sample_waddr
    if (~rst_n) begin
      wdata_a_q        <= '0;
    end else begin
      if(we_a_i)
        wdata_a_q <= wdata_a_i;
    end
  end

  //-----------------------------------------------------------------------------
  //-- WRITE : Write Address Decoder (WAD), combinatorial process
  //-----------------------------------------------------------------------------
  always_comb
  begin : p_WADa
    for(i = 1; i < NUM_WORDS; i++)
    begin : p_WordItera
      if ( (we_a_i == 1'b1 ) && (waddr_a_int == i) )
        waddr_onehot_a[i] = 1'b1;
      else
        waddr_onehot_a[i] = 1'b0;
    end
  end


  //-----------------------------------------------------------------------------
  //-- WRITE : Clock gating (if integrated clock-gating cells are available)
  //-----------------------------------------------------------------------------
  generate
    for(x = 1; x < NUM_WORDS; x++)
    begin : CG_CELL_WORD_ITER
      cluster_clock_gating CG_Inst
      (
        .clk_i     ( clk_int                               ),
        .en_i      ( waddr_onehot_a[x]                     ),
        .test_en_i ( test_en_i                             ),
        .clk_o     ( mem_clocks[x]                         )
      );
    end
  endgenerate

  //-----------------------------------------------------------------------------
  //-- WRITE : Write operation
  //-----------------------------------------------------------------------------
  //-- Generate M = WORDS sequential processes, each of which describes one
  //-- word of the memory. The processes are synchronized with the clocks
  //-- ClocksxC(i), i = 0, 1, ..., M-1
  //-- Use active low, i.e. transparent on low latches as storage elements
  //-- Data is sampled on rising clock edge

  always_latch
  begin : latch_wdata
    // Note: The assignment has to be done inside this process or Modelsim complains about it
    mem[0] = '0;

    for(k = 1; k < NUM_WORDS; k++)
    begin : w_WordIter
      if(mem_clocks[k] == 1'b1)
        mem[k] = wdata_a_q;
    end
  end


endmodule
