// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Testbench module for alert sender/receiver pair. Intended to use with
// a formal tool.

module prim_alert_rxtx_async_fatal_tb
  import prim_alert_pkg::*;
  import prim_mubi_pkg::mubi4_t;
(
  input          clk_i,
  input          rst_ni,
  // for sigint error and skew injection only
  input          ping_err_pi,
  input          ping_err_ni,
  input [1:0]    ping_skew_i,
  input          ack_err_pi,
  input          ack_err_ni,
  input [1:0]    ack_skew_i,
  input          alert_err_pi,
  input          alert_err_ni,
  input [1:0]    alert_skew_i,
  // normal I/Os
  input          alert_test_i,
  input          alert_req_i,
  input  mubi4_t init_trig_i,
  input          ping_req_i,
  output logic   alert_ack_o,
  output logic   alert_state_o,
  output logic   ping_ok_o,
  output logic   integ_fail_o,
  output logic   alert_o
);

  // asynchronous case
  localparam bit AsyncOn = 1'b1;
  localparam bit IsFatal = 1'b1;

  logic ping_pd;
  logic ping_nd;
  logic ack_pd;
  logic ack_nd;
  logic alert_pd;
  logic alert_nd;

  alert_rx_t alert_rx_out, alert_rx_in;
  alert_tx_t alert_tx_out, alert_tx_in;

  // for the purposes of FPV, we currently emulate the asynchronous transition
  // only in terms of the skew it may introduce (which is limited to +- 1 cycle)
  logic [1:0] ping_pq;
  logic [1:0] ping_nq;
  logic [1:0] ack_pq;
  logic [1:0] ack_nq;
  logic [1:0] alert_pq;
  logic [1:0] alert_nq;

  assign ping_pd = alert_rx_out.ping_p;
  assign ping_nd = alert_rx_out.ping_n;
  assign ack_pd  = alert_rx_out.ack_p;
  assign ack_nd  = alert_rx_out.ack_n;
  assign alert_rx_in.ping_p = ping_pq[ping_skew_i[0]] ^ ping_err_pi;
  assign alert_rx_in.ping_n = ping_nq[ping_skew_i[1]] ^ ping_err_ni;
  assign alert_rx_in.ack_p  = ack_pq[ack_skew_i[0]] ^ ack_err_pi;
  assign alert_rx_in.ack_n  = ack_nq[ack_skew_i[1]] ^ ack_err_ni;

  assign alert_pd = alert_tx_out.alert_p;
  assign alert_nd = alert_tx_out.alert_n;
  assign alert_tx_in.alert_p = alert_pq[alert_skew_i[0]] ^ alert_err_pi;
  assign alert_tx_in.alert_n = alert_nq[alert_skew_i[1]] ^ alert_err_ni;

  prim_alert_sender #(
    .AsyncOn ( AsyncOn ),
    .IsFatal ( IsFatal )
  ) i_prim_alert_sender (
    .clk_i,
    .rst_ni,
    .alert_test_i,
    .alert_req_i,
    .alert_ack_o,
    .alert_state_o,
    .alert_rx_i ( alert_rx_in  ),
    .alert_tx_o ( alert_tx_out )
  );

  prim_alert_receiver #(
    .AsyncOn ( AsyncOn )
  ) i_prim_alert_receiver (
    .clk_i,
    .rst_ni,
    .init_trig_i,
    .ping_req_i,
    .ping_ok_o,
    .integ_fail_o,
    .alert_o,
    .alert_rx_o ( alert_rx_out ),
    .alert_tx_i ( alert_tx_in  )
  );

  always_ff @(posedge clk_i or negedge rst_ni) begin : p_skew_delay
    if (!rst_ni) begin
      ping_pq  <= '0;
      ping_nq  <= '1;
      ack_pq   <= '0;
      ack_nq   <= '1;
      alert_pq <= '0;
      alert_nq <= '1;
    end else begin
      ping_pq  <= {ping_pq [$high(ping_pq )-1:0], ping_pd};
      ping_nq  <= {ping_nq [$high(ping_nq )-1:0], ping_nd};
      ack_pq   <= {ack_pq  [$high(ack_pq  )-1:0], ack_pd};
      ack_nq   <= {ack_nq  [$high(ack_nq  )-1:0], ack_nd};
      alert_pq <= {alert_pq[$high(alert_pq)-1:0], alert_pd};
      alert_nq <= {alert_nq[$high(alert_nq)-1:0], alert_nd};
    end
  end

endmodule : prim_alert_rxtx_async_fatal_tb
