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

// Ibex lockstep module
// This module instantiates a second copy of the core logic, and compares it's outputs against
// those from the main core. The second core runs synchronously with the main core, delayed by
// LockstepOffset cycles.

// SEC_CM: LOGIC.SHADOW
module [docs]ibex_lockstep import ibex_pkg::*; #(
  parameter int unsigned            LockstepOffset    = 2,
  parameter bit                     PMPEnable         = 1'b0,
  parameter int unsigned            PMPGranularity    = 0,
  parameter int unsigned            PMPNumRegions     = 4,
  parameter ibex_pkg::pmp_cfg_t     PMPRstCfg[16]     = ibex_pkg::PmpCfgRst,
  parameter logic [33:0]            PMPRstAddr[16]    = ibex_pkg::PmpAddrRst,
  parameter ibex_pkg::pmp_mseccfg_t PMPRstMsecCfg     = ibex_pkg::PmpMseccfgRst,
  parameter int unsigned            MHPMCounterNum    = 0,
  parameter int unsigned            MHPMCounterWidth  = 40,
  parameter bit                     RV32E             = 1'b0,
  parameter rv32m_e                 RV32M             = RV32MFast,
  parameter rv32b_e                 RV32B             = RV32BNone,
  parameter bit                     BranchTargetALU   = 1'b0,
  parameter bit                     WritebackStage    = 1'b0,
  parameter bit                     ICache            = 1'b0,
  parameter bit                     ICacheECC         = 1'b0,
  parameter int unsigned            BusSizeECC        = BUS_SIZE,
  parameter int unsigned            TagSizeECC        = IC_TAG_SIZE,
  parameter int unsigned            LineSizeECC       = IC_LINE_SIZE,
  parameter bit                     BranchPredictor   = 1'b0,
  parameter bit                     DbgTriggerEn      = 1'b0,
  parameter int unsigned            DbgHwBreakNum     = 1,
  parameter bit                     ResetAll          = 1'b0,
  parameter lfsr_seed_t             RndCnstLfsrSeed   = RndCnstLfsrSeedDefault,
  parameter lfsr_perm_t             RndCnstLfsrPerm   = RndCnstLfsrPermDefault,
  parameter bit                     SecureIbex        = 1'b0,
  parameter bit                     DummyInstructions = 1'b0,
  parameter bit                     RegFileECC        = 1'b0,
  parameter int unsigned            RegFileDataWidth  = 32,
  parameter bit                     MemECC            = 1'b0,
  parameter int unsigned            MemDataWidth      = MemECC ? 32 + 7 : 32,
  parameter int unsigned            DmBaseAddr        = 32'h1A110000,
  parameter int unsigned            DmAddrMask        = 32'h00000FFF,
  parameter int unsigned            DmHaltAddr        = 32'h1A110800,
  parameter int unsigned            DmExceptionAddr   = 32'h1A110808
) (
  input  logic                         clk_i,
  input  logic                         rst_ni,

  input  logic [31:0]                  hart_id_i,
  input  logic [31:0]                  boot_addr_i,

  input  logic                         instr_req_i,
  input  logic                         instr_gnt_i,
  input  logic                         instr_rvalid_i,
  input  logic [31:0]                  instr_addr_i,
  input  logic [MemDataWidth-1:0]      instr_rdata_i,
  input  logic                         instr_err_i,

  input  logic                         data_req_i,
  input  logic                         data_gnt_i,
  input  logic                         data_rvalid_i,
  input  logic                         data_we_i,
  input  logic [3:0]                   data_be_i,
  input  logic [31:0]                  data_addr_i,
  input  logic [MemDataWidth-1:0]      data_wdata_i,
  input  logic [MemDataWidth-1:0]      data_rdata_i,
  input  logic                         data_err_i,

  input  logic                         dummy_instr_id_i,
  input  logic                         dummy_instr_wb_i,
  input  logic [4:0]                   rf_raddr_a_i,
  input  logic [4:0]                   rf_raddr_b_i,
  input  logic [4:0]                   rf_waddr_wb_i,
  input  logic                         rf_we_wb_i,
  input  logic [RegFileDataWidth-1:0]  rf_wdata_wb_ecc_i,
  input  logic [RegFileDataWidth-1:0]  rf_rdata_a_ecc_i,
  input  logic [RegFileDataWidth-1:0]  rf_rdata_b_ecc_i,

  input  logic [IC_NUM_WAYS-1:0]       ic_tag_req_i,
  input  logic                         ic_tag_write_i,
  input  logic [IC_INDEX_W-1:0]        ic_tag_addr_i,
  input  logic [TagSizeECC-1:0]        ic_tag_wdata_i,
  input  logic [TagSizeECC-1:0]        ic_tag_rdata_i [IC_NUM_WAYS],
  input  logic [IC_NUM_WAYS-1:0]       ic_data_req_i,
  input  logic                         ic_data_write_i,
  input  logic [IC_INDEX_W-1:0]        ic_data_addr_i,
  input  logic [LineSizeECC-1:0]       ic_data_wdata_i,
  input  logic [LineSizeECC-1:0]       ic_data_rdata_i [IC_NUM_WAYS],
  input  logic                         ic_scr_key_valid_i,
  input  logic                         ic_scr_key_req_i,

  input  logic                         irq_software_i,
  input  logic                         irq_timer_i,
  input  logic                         irq_external_i,
  input  logic [14:0]                  irq_fast_i,
  input  logic                         irq_nm_i,
  input  logic                         irq_pending_i,

  input  logic                         debug_req_i,
  input  crash_dump_t                  crash_dump_i,
  input  logic                         double_fault_seen_i,

  input  ibex_mubi_t                   fetch_enable_i,
  output logic                         alert_minor_o,
  output logic                         alert_major_internal_o,
  output logic                         alert_major_bus_o,
  input  ibex_mubi_t                   core_busy_i,
  input  logic                         test_en_i,
  input  logic                         scan_rst_ni
);

  localparam int unsigned LockstepOffsetW = $clog2(LockstepOffset);
  // Core outputs are delayed for an extra cycle due to shadow output registers
  localparam int unsigned OutputsOffset = LockstepOffset + 1;

  //////////////////////
  // Reset generation //
  //////////////////////

  // Upon reset, the comparison is stopped and the shadow core is reset, both immediately. A
  // counter is started. After LockstepOffset clock cycles:
  // - The counter is stopped.
  // - The reset of the shadow core is synchronously released.
  // The comparison is started in the following clock cycle.

  logic [LockstepOffsetW-1:0] rst_shadow_cnt;
  logic                       rst_shadow_cnt_err;
  ibex_mubi_t                 rst_shadow_set_d, rst_shadow_set_q;
  logic                       rst_shadow_n, rst_shadow_set_single_bit;
  ibex_mubi_t                 enable_cmp_d, enable_cmp_q;

  // This counter primitive starts counting to LockstepOffset after a system
  // reset. The counter value saturates at LockstepOffset.
  prim_count #(
    .Width      (LockstepOffsetW        ),
    .ResetValue (LockstepOffsetW'(1'b0) )
  ) u_rst_shadow_cnt (
    .clk_i              (clk_i                  ),
    .rst_ni             (rst_ni                 ),
    .clr_i              (1'b0                   ),
    .set_i              (1'b0                   ),
    .set_cnt_i          ('0                     ),
    .incr_en_i          (1'b1                   ),
    .decr_en_i          (1'b0                   ),
    .step_i             (LockstepOffsetW'(1'b1) ),
    .commit_i           (1'b1                   ),
    .cnt_o              (rst_shadow_cnt         ),
    .cnt_after_commit_o (                       ),
    .err_o              (rst_shadow_cnt_err     )
  );

  // When the LockstepOffset counter value is reached, activate the lockstep
  // comparison. We do not explicitly check whether rst_shadow_set_q forms a valid
  // multibit signal as this value is implicitly checked by the enable_cmp
  // comparison below.
  assign rst_shadow_set_d =
    (rst_shadow_cnt >= LockstepOffsetW'(LockstepOffset - 1)) ? IbexMuBiOn : IbexMuBiOff;

  // Enable lockstep comparison.
  assign enable_cmp_d = rst_shadow_set_q;

  // This assignment is needed in order to avoid "Warning-IMPERFECTSCH" messages.
  // TODO: Remove when updating Verilator #2134.
  assign rst_shadow_set_single_bit = rst_shadow_set_q[0];

  // The primitives below are used to place size-only constraints in order to prevent
  // synthesis optimizations and preserve anchor points for constraining backend tools.
  prim_flop #(
    .Width(IbexMuBiWidth),
    .ResetValue(IbexMuBiOff)
  ) u_prim_rst_shadow_set_flop (
    .clk_i (clk_i),
    .rst_ni(rst_ni),
    .d_i   (rst_shadow_set_d),
    .q_o   (rst_shadow_set_q)
  );

  prim_flop #(
    .Width(IbexMuBiWidth),
    .ResetValue(IbexMuBiOff)
  ) u_prim_enable_cmp_flop (
    .clk_i (clk_i),
    .rst_ni(rst_ni),
    .d_i   (enable_cmp_d),
    .q_o   (enable_cmp_q)
  );

  prim_clock_mux2 #(
    .NoFpgaBufG(1'b1)
  ) u_prim_rst_shadow_n_mux2 (
    .clk0_i(rst_shadow_set_single_bit),
    .clk1_i(scan_rst_ni),
    .sel_i (test_en_i),
    .clk_o (rst_shadow_n)
  );

  //////////////////
  // Input delays //
  //////////////////

  typedef struct packed {
    logic                        instr_gnt;
    logic                        instr_rvalid;
    logic [MemDataWidth-1:0]     instr_rdata;
    logic                        instr_err;
    logic                        data_gnt;
    logic                        data_rvalid;
    logic [MemDataWidth-1:0]     data_rdata;
    logic                        data_err;
    logic [RegFileDataWidth-1:0] rf_rdata_a_ecc;
    logic [RegFileDataWidth-1:0] rf_rdata_b_ecc;
    logic                        irq_software;
    logic                        irq_timer;
    logic                        irq_external;
    logic [14:0]                 irq_fast;
    logic                        irq_nm;
    logic                        debug_req;
    ibex_mubi_t                  fetch_enable;
    logic                        ic_scr_key_valid;
  } delayed_inputs_t;

  delayed_inputs_t [LockstepOffset-1:0] shadow_inputs_q;
  delayed_inputs_t                      shadow_inputs_in;
  // Packed arrays must be dealt with separately
  logic [TagSizeECC-1:0]                shadow_tag_rdata_q [IC_NUM_WAYS][LockstepOffset];
  logic [LineSizeECC-1:0]               shadow_data_rdata_q [IC_NUM_WAYS][LockstepOffset];

  // Assign the inputs to the delay structure
  assign shadow_inputs_in.instr_gnt        = instr_gnt_i;
  assign shadow_inputs_in.instr_rvalid     = instr_rvalid_i;
  assign shadow_inputs_in.instr_rdata      = instr_rdata_i;
  assign shadow_inputs_in.instr_err        = instr_err_i;
  assign shadow_inputs_in.data_gnt         = data_gnt_i;
  assign shadow_inputs_in.data_rvalid      = data_rvalid_i;
  assign shadow_inputs_in.data_rdata       = data_rdata_i;
  assign shadow_inputs_in.data_err         = data_err_i;
  assign shadow_inputs_in.rf_rdata_a_ecc   = rf_rdata_a_ecc_i;
  assign shadow_inputs_in.rf_rdata_b_ecc   = rf_rdata_b_ecc_i;
  assign shadow_inputs_in.irq_software     = irq_software_i;
  assign shadow_inputs_in.irq_timer        = irq_timer_i;
  assign shadow_inputs_in.irq_external     = irq_external_i;
  assign shadow_inputs_in.irq_fast         = irq_fast_i;
  assign shadow_inputs_in.irq_nm           = irq_nm_i;
  assign shadow_inputs_in.debug_req        = debug_req_i;
  assign shadow_inputs_in.fetch_enable     = fetch_enable_i;
  assign shadow_inputs_in.ic_scr_key_valid = ic_scr_key_valid_i;

  // Delay the inputs
  always_ff[docs] @(posedge clk_i or negedge rst_ni) begin
    if (!rst_ni) begin
      for (int unsigned i = 0; i < LockstepOffset; i++) begin
        shadow_inputs_q[i]     <= delayed_inputs_t'('0);
        shadow_tag_rdata_q[i]  <= '{default: 0};
        shadow_data_rdata_q[i] <= '{default: 0};
      end
    end else begin
      for (int unsigned i = 0; i < LockstepOffset - 1; i++) begin
        shadow_inputs_q[i]     <= shadow_inputs_q[i+1];
        shadow_tag_rdata_q[i]  <= shadow_tag_rdata_q[i+1];
        shadow_data_rdata_q[i] <= shadow_data_rdata_q[i+1];
      end
      shadow_inputs_q[LockstepOffset-1]     <= shadow_inputs_in;
      shadow_tag_rdata_q[LockstepOffset-1]  <= ic_tag_rdata_i;
      shadow_data_rdata_q[LockstepOffset-1] <= ic_data_rdata_i;
    end
  end

  ///////////////////
  // Output delays //
  ///////////////////

  typedef struct packed {
    logic                        instr_req;
    logic [31:0]                 instr_addr;
    logic                        data_req;
    logic                        data_we;
    logic [3:0]                  data_be;
    logic [31:0]                 data_addr;
    logic [MemDataWidth-1:0]     data_wdata;
    logic                        dummy_instr_id;
    logic                        dummy_instr_wb;
    logic [4:0]                  rf_raddr_a;
    logic [4:0]                  rf_raddr_b;
    logic [4:0]                  rf_waddr_wb;
    logic                        rf_we_wb;
    logic [RegFileDataWidth-1:0] rf_wdata_wb_ecc;
    logic [IC_NUM_WAYS-1:0]      ic_tag_req;
    logic                        ic_tag_write;
    logic [IC_INDEX_W-1:0]       ic_tag_addr;
    logic [TagSizeECC-1:0]       ic_tag_wdata;
    logic [IC_NUM_WAYS-1:0]      ic_data_req;
    logic                        ic_data_write;
    logic [IC_INDEX_W-1:0]       ic_data_addr;
    logic [LineSizeECC-1:0]      ic_data_wdata;
    logic                        ic_scr_key_req;
    logic                        irq_pending;
    crash_dump_t                 crash_dump;
    logic                        double_fault_seen;
    ibex_mubi_t                  core_busy;
  } delayed_outputs_t;

  delayed_outputs_t [OutputsOffset-1:0]  core_outputs_q;
  delayed_outputs_t                      core_outputs_in;
  delayed_outputs_t                      shadow_outputs_d, shadow_outputs_q;

  // Assign core outputs to the structure
  assign core_outputs_in.instr_req           = instr_req_i;
  assign core_outputs_in.instr_addr          = instr_addr_i;
  assign core_outputs_in.data_req            = data_req_i;
  assign core_outputs_in.data_we             = data_we_i;
  assign core_outputs_in.data_be             = data_be_i;
  assign core_outputs_in.data_addr           = data_addr_i;
  assign core_outputs_in.data_wdata          = data_wdata_i;
  assign core_outputs_in.dummy_instr_id      = dummy_instr_id_i;
  assign core_outputs_in.dummy_instr_wb      = dummy_instr_wb_i;
  assign core_outputs_in.rf_raddr_a          = rf_raddr_a_i;
  assign core_outputs_in.rf_raddr_b          = rf_raddr_b_i;
  assign core_outputs_in.rf_waddr_wb         = rf_waddr_wb_i;
  assign core_outputs_in.rf_we_wb            = rf_we_wb_i;
  assign core_outputs_in.rf_wdata_wb_ecc     = rf_wdata_wb_ecc_i;
  assign core_outputs_in.ic_tag_req          = ic_tag_req_i;
  assign core_outputs_in.ic_tag_write        = ic_tag_write_i;
  assign core_outputs_in.ic_tag_addr         = ic_tag_addr_i;
  assign core_outputs_in.ic_tag_wdata        = ic_tag_wdata_i;
  assign core_outputs_in.ic_data_req         = ic_data_req_i;
  assign core_outputs_in.ic_data_write       = ic_data_write_i;
  assign core_outputs_in.ic_data_addr        = ic_data_addr_i;
  assign core_outputs_in.ic_data_wdata       = ic_data_wdata_i;
  assign core_outputs_in.ic_scr_key_req      = ic_scr_key_req_i;
  assign core_outputs_in.irq_pending         = irq_pending_i;
  assign core_outputs_in.crash_dump          = crash_dump_i;
  assign core_outputs_in.double_fault_seen   = double_fault_seen_i;
  assign core_outputs_in.core_busy           = core_busy_i;

  // Delay the outputs
  always_ff[docs] @(posedge clk_i) begin
    for (int unsigned i = 0; i < OutputsOffset - 1; i++) begin
      core_outputs_q[i] <= core_outputs_q[i+1];
    end
    core_outputs_q[OutputsOffset-1] <= core_outputs_in;
  end

  ///////////////////////////////
  // Shadow core instantiation //
  ///////////////////////////////

  logic shadow_alert_minor, shadow_alert_major_internal, shadow_alert_major_bus;

  ibex_core #(
    .PMPEnable         ( PMPEnable         ),
    .PMPGranularity    ( PMPGranularity    ),
    .PMPNumRegions     ( PMPNumRegions     ),
    .PMPRstCfg         ( PMPRstCfg         ),
    .PMPRstAddr        ( PMPRstAddr        ),
    .PMPRstMsecCfg     ( PMPRstMsecCfg     ),
    .MHPMCounterNum    ( MHPMCounterNum    ),
    .MHPMCounterWidth  ( MHPMCounterWidth  ),
    .RV32E             ( RV32E             ),
    .RV32M             ( RV32M             ),
    .RV32B             ( RV32B             ),
    .BranchTargetALU   ( BranchTargetALU   ),
    .ICache            ( ICache            ),
    .ICacheECC         ( ICacheECC         ),
    .BusSizeECC        ( BusSizeECC        ),
    .TagSizeECC        ( TagSizeECC        ),
    .LineSizeECC       ( LineSizeECC       ),
    .BranchPredictor   ( BranchPredictor   ),
    .DbgTriggerEn      ( DbgTriggerEn      ),
    .DbgHwBreakNum     ( DbgHwBreakNum     ),
    .WritebackStage    ( WritebackStage    ),
    .ResetAll          ( ResetAll          ),
    .RndCnstLfsrSeed   ( RndCnstLfsrSeed   ),
    .RndCnstLfsrPerm   ( RndCnstLfsrPerm   ),
    .SecureIbex        ( SecureIbex        ),
    .DummyInstructions ( DummyInstructions ),
    .RegFileECC        ( RegFileECC        ),
    .RegFileDataWidth  ( RegFileDataWidth  ),
    .MemECC            ( MemECC            ),
    .MemDataWidth      ( MemDataWidth      ),
    .DmBaseAddr        ( DmBaseAddr        ),
    .DmAddrMask        ( DmAddrMask        ),
    .DmHaltAddr        ( DmHaltAddr        ),
    .DmExceptionAddr   ( DmExceptionAddr   )
  ) u_shadow_core (
    .clk_i               (clk_i),
    .rst_ni              (rst_shadow_n),

    .hart_id_i           (hart_id_i),
    .boot_addr_i         (boot_addr_i),

    .instr_req_o         (shadow_outputs_d.instr_req),
    .instr_gnt_i         (shadow_inputs_q[0].instr_gnt),
    .instr_rvalid_i      (shadow_inputs_q[0].instr_rvalid),
    .instr_addr_o        (shadow_outputs_d.instr_addr),
    .instr_rdata_i       (shadow_inputs_q[0].instr_rdata),
    .instr_err_i         (shadow_inputs_q[0].instr_err),

    .data_req_o          (shadow_outputs_d.data_req),
    .data_gnt_i          (shadow_inputs_q[0].data_gnt),
    .data_rvalid_i       (shadow_inputs_q[0].data_rvalid),
    .data_we_o           (shadow_outputs_d.data_we),
    .data_be_o           (shadow_outputs_d.data_be),
    .data_addr_o         (shadow_outputs_d.data_addr),
    .data_wdata_o        (shadow_outputs_d.data_wdata),
    .data_rdata_i        (shadow_inputs_q[0].data_rdata),
    .data_err_i          (shadow_inputs_q[0].data_err),

    .dummy_instr_id_o    (shadow_outputs_d.dummy_instr_id),
    .dummy_instr_wb_o    (shadow_outputs_d.dummy_instr_wb),
    .rf_raddr_a_o        (shadow_outputs_d.rf_raddr_a),
    .rf_raddr_b_o        (shadow_outputs_d.rf_raddr_b),
    .rf_waddr_wb_o       (shadow_outputs_d.rf_waddr_wb),
    .rf_we_wb_o          (shadow_outputs_d.rf_we_wb),
    .rf_wdata_wb_ecc_o   (shadow_outputs_d.rf_wdata_wb_ecc),
    .rf_rdata_a_ecc_i    (shadow_inputs_q[0].rf_rdata_a_ecc),
    .rf_rdata_b_ecc_i    (shadow_inputs_q[0].rf_rdata_b_ecc),

    .ic_tag_req_o        (shadow_outputs_d.ic_tag_req),
    .ic_tag_write_o      (shadow_outputs_d.ic_tag_write),
    .ic_tag_addr_o       (shadow_outputs_d.ic_tag_addr),
    .ic_tag_wdata_o      (shadow_outputs_d.ic_tag_wdata),
    .ic_tag_rdata_i      (shadow_tag_rdata_q[0]),
    .ic_data_req_o       (shadow_outputs_d.ic_data_req),
    .ic_data_write_o     (shadow_outputs_d.ic_data_write),
    .ic_data_addr_o      (shadow_outputs_d.ic_data_addr),
    .ic_data_wdata_o     (shadow_outputs_d.ic_data_wdata),
    .ic_data_rdata_i     (shadow_data_rdata_q[0]),
    .ic_scr_key_valid_i  (shadow_inputs_q[0].ic_scr_key_valid),
    .ic_scr_key_req_o    (shadow_outputs_d.ic_scr_key_req),

    .irq_software_i      (shadow_inputs_q[0].irq_software),
    .irq_timer_i         (shadow_inputs_q[0].irq_timer),
    .irq_external_i      (shadow_inputs_q[0].irq_external),
    .irq_fast_i          (shadow_inputs_q[0].irq_fast),
    .irq_nm_i            (shadow_inputs_q[0].irq_nm),
    .irq_pending_o       (shadow_outputs_d.irq_pending),

    .debug_req_i         (shadow_inputs_q[0].debug_req),
    .crash_dump_o        (shadow_outputs_d.crash_dump),
    .double_fault_seen_o (shadow_outputs_d.double_fault_seen),

`ifdef RVFI
    .rvfi_valid                (),
    .rvfi_order                (),
    .rvfi_insn                 (),
    .rvfi_trap                 (),
    .rvfi_halt                 (),
    .rvfi_intr                 (),
    .rvfi_mode                 (),
    .rvfi_ixl                  (),
    .rvfi_rs1_addr             (),
    .rvfi_rs2_addr             (),
    .rvfi_rs3_addr             (),
    .rvfi_rs1_rdata            (),
    .rvfi_rs2_rdata            (),
    .rvfi_rs3_rdata            (),
    .rvfi_rd_addr              (),
    .rvfi_rd_wdata             (),
    .rvfi_pc_rdata             (),
    .rvfi_pc_wdata             (),
    .rvfi_mem_addr             (),
    .rvfi_mem_rmask            (),
    .rvfi_mem_wmask            (),
    .rvfi_mem_rdata            (),
    .rvfi_mem_wdata            (),
    .rvfi_ext_pre_mip          (),
    .rvfi_ext_post_mip         (),
    .rvfi_ext_nmi              (),
    .rvfi_ext_nmi_int          (),
    .rvfi_ext_debug_req        (),
    .rvfi_ext_debug_mode       (),
    .rvfi_ext_rf_wr_suppress   (),
    .rvfi_ext_mcycle           (),
    .rvfi_ext_mhpmcounters     (),
    .rvfi_ext_mhpmcountersh    (),
    .rvfi_ext_ic_scr_key_valid (),
    .rvfi_ext_irq_valid        (),
`endif

    .fetch_enable_i         (shadow_inputs_q[0].fetch_enable),
    .alert_minor_o          (shadow_alert_minor),
    .alert_major_internal_o (shadow_alert_major_internal),
    .alert_major_bus_o      (shadow_alert_major_bus),
    .core_busy_o            (shadow_outputs_d.core_busy)
  );

  // Register the shadow core outputs
  always_ff[docs] @(posedge clk_i) begin
    shadow_outputs_q <= shadow_outputs_d;
  end

  /////////////////////////
  // Compare the outputs //
  /////////////////////////

  logic outputs_mismatch;

  assign outputs_mismatch =
    (enable_cmp_q != IbexMuBiOff) & (shadow_outputs_q != core_outputs_q[0]);
  assign alert_major_internal_o
    = outputs_mismatch | shadow_alert_major_internal | rst_shadow_cnt_err;
  assign alert_major_bus_o      = shadow_alert_major_bus;
  assign alert_minor_o          = shadow_alert_minor;

endmodule