mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 12:57:13 -04:00
This commit protects the core_busy_o signal using a multi-bit encoding to reduce the chances of an adversary for glitching this signal to low, thereby putting the core to sleep and e.g. not handling an alert. Without this commit, the glitch would only be detected once both the main core and the shadow core wake up again and the comparison of the core_busy_o signals continues. This resolves lowRISC/Ibex#1827. Signed-off-by: Pirmin Vogel <vogelpi@lowrisc.org>
458 lines
19 KiB
Systemverilog
458 lines
19 KiB
Systemverilog
// 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 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 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 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 [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_d, rst_shadow_cnt_q, rst_shadow_cnt_incr;
|
|
// Internally generated resets cause IMPERFECTSCH warnings
|
|
/* verilator lint_off IMPERFECTSCH */
|
|
logic rst_shadow_set_d, rst_shadow_set_q;
|
|
logic rst_shadow_n, enable_cmp_q;
|
|
/* verilator lint_on IMPERFECTSCH */
|
|
|
|
assign rst_shadow_cnt_incr = rst_shadow_cnt_q + 1'b1;
|
|
|
|
assign rst_shadow_set_d = (rst_shadow_cnt_q == LockstepOffsetW'(LockstepOffset - 1));
|
|
assign rst_shadow_cnt_d = rst_shadow_set_d ? rst_shadow_cnt_q : rst_shadow_cnt_incr;
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin
|
|
if (!rst_ni) begin
|
|
rst_shadow_cnt_q <= '0;
|
|
enable_cmp_q <= '0;
|
|
end else begin
|
|
rst_shadow_cnt_q <= rst_shadow_cnt_d;
|
|
enable_cmp_q <= rst_shadow_set_q;
|
|
end
|
|
end
|
|
|
|
// 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(1),
|
|
.ResetValue(1'b0)
|
|
) 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_clock_mux2 #(
|
|
.NoFpgaBufG(1'b1)
|
|
) u_prim_rst_shadow_n_mux2 (
|
|
.clk0_i(rst_shadow_set_q),
|
|
.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 @(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 [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.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 @(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 ),
|
|
.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 ),
|
|
.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),
|
|
.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_mip (),
|
|
.rvfi_ext_nmi (),
|
|
.rvfi_ext_debug_req (),
|
|
.rvfi_ext_mcycle (),
|
|
.rvfi_ext_mhpmcounters (),
|
|
.rvfi_ext_mhpmcountersh (),
|
|
.rvfi_ext_ic_scr_key_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 @(posedge clk_i) begin
|
|
shadow_outputs_q <= shadow_outputs_d;
|
|
end
|
|
|
|
/////////////////////////
|
|
// Compare the outputs //
|
|
/////////////////////////
|
|
|
|
logic outputs_mismatch;
|
|
|
|
assign outputs_mismatch = enable_cmp_q & (shadow_outputs_q != core_outputs_q[0]);
|
|
assign alert_major_internal_o = outputs_mismatch | shadow_alert_major_internal;
|
|
assign alert_major_bus_o = shadow_alert_major_bus;
|
|
assign alert_minor_o = shadow_alert_minor;
|
|
|
|
endmodule
|