| // 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. |
| 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 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 [31:0] instr_rdata_i, |
| input logic [6:0] instr_rdata_intg_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 [31:0] data_wdata_i, |
| output logic [6:0] data_wdata_intg_o, |
| input logic [31:0] data_rdata_i, |
| input logic [6:0] data_rdata_intg_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 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 fetch_enable_i, |
| output logic alert_minor_o, |
| output logic alert_major_o, |
| input logic 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 // |
| ////////////////////// |
| |
| logic [LockstepOffsetW-1:0] rst_shadow_cnt_d, rst_shadow_cnt_q; |
| // 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_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_q + LockstepOffsetW'(1)); |
| |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| rst_shadow_cnt_q <= '0; |
| rst_shadow_set_q <= '0; |
| enable_cmp_q <= '0; |
| end else begin |
| rst_shadow_cnt_q <= rst_shadow_cnt_d; |
| rst_shadow_set_q <= rst_shadow_set_d; |
| enable_cmp_q <= rst_shadow_set_q; |
| end |
| end |
| |
| assign rst_shadow_n = test_en_i ? scan_rst_ni : rst_shadow_set_q; |
| |
| ////////////////// |
| // Input delays // |
| ////////////////// |
| |
| typedef struct packed { |
| logic instr_gnt; |
| logic instr_rvalid; |
| logic [31:0] instr_rdata; |
| logic instr_err; |
| logic data_gnt; |
| logic data_rvalid; |
| logic [31: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; |
| logic fetch_enable; |
| } delayed_inputs_t; |
| |
| delayed_inputs_t [LockstepOffset-1:0] shadow_inputs_q; |
| delayed_inputs_t shadow_inputs_in; |
| logic [6:0] instr_rdata_intg_q, data_rdata_intg_q; |
| // 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; |
| |
| // Delay the inputs |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| instr_rdata_intg_q <= '0; |
| data_rdata_intg_q <= '0; |
| 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 |
| instr_rdata_intg_q <= instr_rdata_intg_i; |
| data_rdata_intg_q <= data_rdata_intg_i; |
| 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 |
| |
| //////////////////////////// |
| // Bus integrity checking // |
| //////////////////////////// |
| |
| logic bus_intg_err; |
| logic [1:0] instr_intg_err, data_intg_err; |
| logic [31:0] unused_wdata; |
| |
| // Checks on incoming data |
| prim_secded_inv_39_32_dec u_instr_intg_dec ( |
| .data_i ({instr_rdata_intg_q, shadow_inputs_q[LockstepOffset-1].instr_rdata}), |
| .data_o (), |
| .syndrome_o (), |
| .err_o (instr_intg_err) |
| ); |
| |
| prim_secded_inv_39_32_dec u_data_intg_dec ( |
| .data_i ({data_rdata_intg_q, shadow_inputs_q[LockstepOffset-1].data_rdata}), |
| .data_o (), |
| .syndrome_o (), |
| .err_o (data_intg_err) |
| ); |
| |
| assign bus_intg_err = (shadow_inputs_q[LockstepOffset-1].instr_rvalid & |instr_intg_err) | |
| (shadow_inputs_q[LockstepOffset-1].data_rvalid & |data_intg_err); |
| |
| // Generate integrity bits |
| prim_secded_inv_39_32_enc u_data_gen ( |
| .data_i (data_wdata_i), |
| .data_o ({data_wdata_intg_o, unused_wdata}) |
| ); |
| |
| /////////////////// |
| // 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 [31: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 irq_pending; |
| crash_dump_t crash_dump; |
| logic 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.irq_pending = irq_pending_i; |
| assign core_outputs_in.crash_dump = crash_dump_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; |
| |
| 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 ), |
| .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]), |
| |
| .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), |
| |
| `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 (), |
| `endif |
| |
| .fetch_enable_i (shadow_inputs_q[0].fetch_enable), |
| .alert_minor_o (shadow_alert_minor), |
| .alert_major_o (shadow_alert_major), |
| .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_o = outputs_mismatch | shadow_alert_major | bus_intg_err; |
| assign alert_minor_o = shadow_alert_minor; |
| |
| endmodule |