//
// Copyright 2020 OpenHW Group
// Copyright 2020 Silicon Labs, Inc.
//
// Licensed under the Solderpad Hardware Licence, 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
//
//     https://solderpad.org/licenses/
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
// riscv_gnt_stall.sv
//
// OBI Grant stalling module for CV32E40P
//
// Author: Steve Richmond
//   email: steve.richmond@silabs.com
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////


module riscv_gnt_stall #(
    parameter MAX_STALL_N    = 1,
              RAM_ADDR_WIDTH = 32,
              DATA_WIDTH     = 32
) (
    input logic clk_i,
    input logic rst_ni,

    input  logic req_core_i,
    output logic req_mem_o,

    // grant to memory
    output logic grant_core_o,
    input  logic grant_mem_i,

    input logic        en_stall_i,
    input logic [31:0] stall_mode_i,
    input logic [31:0] max_stall_i,
    input logic [31:0] gnt_stall_i
);

  // -----------------------------------------------------------------------------------------------
  // Local variables
  // -----------------------------------------------------------------------------------------------


  import cv32e40p_pkg::*;
  import perturbation_pkg::*;

  logic   req_core_i_q;
  logic   grant_core_o_q;

  integer grant_delay_cnt;

  integer delay_value;

  // -----------------------------------------------------------------------------------------------
  // Tasks and functions
  // -----------------------------------------------------------------------------------------------
  task set_delay_value();
`ifndef VERILATOR
    if (!en_stall_i) delay_value = 0;
    else if (stall_mode_i == perturbation_pkg::STANDARD) delay_value = gnt_stall_i;
    else if (stall_mode_i == perturbation_pkg::RANDOM) delay_value = $urandom_range(max_stall_i, 0);
    else delay_value = 0;
`else
    delay_value = 0;
`endif
  endtask : set_delay_value

  // -----------------------------------------------------------------------------------------------
  // Begin module code
  // -----------------------------------------------------------------------------------------------

  assign req_mem_o = req_core_i;

  always @(posedge clk_i or negedge rst_ni) begin
    if (!rst_ni) begin
      req_core_i_q   <= 1'b0;
      grant_core_o_q <= 1'b0;
    end else begin
      req_core_i_q   <= req_core_i;
      grant_core_o_q <= grant_core_o;
    end
  end

  always @(posedge clk_i or negedge rst_ni) begin
    if (!rst_ni) begin
      grant_core_o <= 1'b0;
      grant_delay_cnt <= 0;
    end else begin
`ifdef VERILATOR
      //#1;
`else
      #(100ps);
`endif

      // When request is removed, remove grant
      if (!req_core_i) begin
        grant_core_o <= $urandom;
      end

    // New request coming in
    else
      if (grant_core_o_q || !req_core_i_q) begin
        // Initialize stall here
        set_delay_value();
        if (delay_value == 0) begin
          grant_delay_cnt <= 0;
          grant_core_o <= 1'b1;
        end else begin
          grant_delay_cnt <= delay_value;
          grant_core_o <= 1'b0;
        end
      end else if (grant_delay_cnt == 1) begin
        grant_delay_cnt <= 0;
        grant_core_o <= 1'b1;
      end else begin
        grant_delay_cnt <= grant_delay_cnt - 1;
      end
    end
  end

endmodule : riscv_gnt_stall
