diff --git a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv index ea0b2e82c..65fe39939 100644 --- a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv +++ b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv @@ -31,6 +31,7 @@ module ibex_riscv_compliance ( parameter bit ICacheECC = 1'b0; parameter bit BranchPredictor = 1'b0; parameter bit SecureIbex = 1'b0; + parameter int unsigned LockstepOffset = 1; parameter bit ICacheScramble = 1'b0; parameter bit DbgTriggerEn = 1'b0; @@ -156,6 +157,7 @@ module ibex_riscv_compliance ( .BranchPredictor (BranchPredictor ), .DbgTriggerEn (DbgTriggerEn ), .SecureIbex (SecureIbex ), + .LockstepOffset (LockstepOffset ), .ICacheScramble (ICacheScramble ), .DmBaseAddr (32'h00000000 ), .DmAddrMask (32'h00000003 ), diff --git a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv index b4107f41a..7b5cf1054 100644 --- a/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv +++ b/dv/uvm/core_ibex/tb/core_ibex_tb_top.sv @@ -68,6 +68,7 @@ module core_ibex_tb_top; parameter bit ICacheECC = 1'b0; parameter bit BranchPredictor = 1'b0; parameter bit SecureIbex = 1'b0; + parameter int unsigned LockstepOffset = 1; parameter bit ICacheScramble = 1'b0; parameter bit DbgTriggerEn = 1'b0; parameter int unsigned DmBaseAddr = 32'h`DM_ADDR; @@ -107,6 +108,7 @@ module core_ibex_tb_top; .ICache (ICache ), .ICacheECC (ICacheECC ), .SecureIbex (SecureIbex ), + .LockstepOffset (LockstepOffset ), .ICacheScramble (ICacheScramble ), .BranchPredictor (BranchPredictor ), .DbgTriggerEn (DbgTriggerEn ), @@ -377,9 +379,10 @@ module core_ibex_tb_top; end // Manually set unused_assert_connected = 1 to disable the AssertConnected_A assertion for - // prim_count in case lockstep (set by SecureIbex) is enabled. If not disabled, DV fails. - if (SecureIbex) begin : gen_disable_count_check - assign dut.u_ibex_top.gen_lockstep.u_ibex_lockstep.u_rst_shadow_cnt. + // prim_count in case lockstep (set by SecureIbex) is enabled and the lockstep offset is + // larger than 1. If not disabled, DV fails. + if (SecureIbex && LockstepOffset > 1) begin : gen_disable_count_check + assign dut.u_ibex_top.gen_lockstep.u_ibex_lockstep.gen_reset_counter.u_rst_shadow_cnt. unused_assert_connected = 1; end diff --git a/examples/simple_system/rtl/ibex_simple_system.sv b/examples/simple_system/rtl/ibex_simple_system.sv index 89ef13fe7..f5063c577 100644 --- a/examples/simple_system/rtl/ibex_simple_system.sv +++ b/examples/simple_system/rtl/ibex_simple_system.sv @@ -44,6 +44,7 @@ module ibex_simple_system ( ); parameter bit SecureIbex = 1'b0; + parameter int unsigned LockstepOffset = 1; parameter bit ICacheScramble = 1'b0; parameter bit PMPEnable = 1'b0; parameter int unsigned PMPGranularity = 0; @@ -197,6 +198,7 @@ module ibex_simple_system ( ibex_top_tracing #( .SecureIbex ( SecureIbex ), + .LockstepOffset ( LockstepOffset ), .ICacheScramble ( ICacheScramble ), .PMPEnable ( PMPEnable ), .PMPGranularity ( PMPGranularity ), diff --git a/ibex_top.core b/ibex_top.core index 525973718..b772066a9 100644 --- a/ibex_top.core +++ b/ibex_top.core @@ -18,6 +18,7 @@ filesets: - lowrisc:prim:ram_1p_scr - lowrisc:prim:onehot_check - lowrisc:prim:onehot + - lowrisc:prim:util files: - rtl/ibex_register_file_ff.sv # generic FF-based - rtl/ibex_register_file_fpga.sv # FPGA diff --git a/rtl/ibex_lockstep.sv b/rtl/ibex_lockstep.sv index 61dc450d6..ff1c45dad 100644 --- a/rtl/ibex_lockstep.sv +++ b/rtl/ibex_lockstep.sv @@ -9,7 +9,7 @@ // SEC_CM: LOGIC.SHADOW module ibex_lockstep import ibex_pkg::*; #( - parameter int unsigned LockstepOffset = 2, + parameter int unsigned LockstepOffset = 1, parameter bit PMPEnable = 1'b0, parameter int unsigned PMPGranularity = 0, parameter int unsigned PMPNumRegions = 4, @@ -116,55 +116,75 @@ module ibex_lockstep import ibex_pkg::*; #( input logic scan_rst_ni ); - localparam int unsigned LockstepOffsetW = $clog2(LockstepOffset); + localparam int unsigned LockstepOffsetW = prim_util_pkg::vbits(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; ibex_mubi_t enable_cmp_d, enable_cmp_q; + if (LockstepOffset > 1) begin : gen_reset_counter + // 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; + // 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. + assign rst_shadow_set_d = + (rst_shadow_cnt >= LockstepOffsetW'(LockstepOffset - 1)) ? IbexMuBiOn : IbexMuBiOff; + + // Enable lockstep comparison. + assign enable_cmp_d = rst_shadow_set_q; + + end else begin : gen_no_reset_counter + // Assert rst_shadow_set_d as soon as we have the first clk_i and rst_ni posedge. + // The reset of the shadow core will then be enabled with a delay of 1 cycle with + // rst_shadow_set_q. + assign rst_shadow_set_d = IbexMuBiOn; + + // Tie off this error as it is not used in the `LockstepOffset = 1` case. + assign rst_shadow_cnt_err = 1'b0; + + // Delay the comparison enable signal by 1 cycle. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + enable_cmp_d <= IbexMuBiOff; + end else begin + enable_cmp_d <= IbexMuBiOn; + end + end - // 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; + // Tie off the upper unused bits of rst_shadow_set_q. + logic [2:0] unused_bits; + assign unused_bits = rst_shadow_set_q[3:1]; + 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. @@ -224,9 +244,62 @@ module ibex_lockstep import ibex_pkg::*; #( 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]; + logic [TagSizeECC-1:0] shadow_tag_rdata_delayed [IC_NUM_WAYS]; + logic [LineSizeECC-1:0] shadow_data_rdata_delayed [IC_NUM_WAYS]; + if (LockstepOffset > 1) begin : gen_multi_cycle_delay + 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 shadow_tag_rdata_delayed = shadow_tag_rdata_q[0]; + assign shadow_data_rdata_delayed = shadow_data_rdata_q[0]; + + // Delay the ic_tag_rdata_i and ic_data_rdata_i inputs by LockstepOffset cycles. + 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 + end else begin : gen_single_cycle_delay + // If LockstepOffset = 1, using: + // logic [TagSizeECC-1:0] shadow_tag_rdata_q [IC_NUM_WAYS][LockstepOffset]; + // logic [TagSizeECC-1:0] shadow_tag_rdata_q [IC_NUM_WAYS][LockstepOffset]; + // aborts the compilation with an error message: + // `port or terminal connection type check failed on instance` + // Hence, in this case, remove the unpacked array dimension. + logic [TagSizeECC-1:0] shadow_tag_rdata_q [IC_NUM_WAYS]; + logic [LineSizeECC-1:0] shadow_data_rdata_q [IC_NUM_WAYS]; + + assign shadow_tag_rdata_delayed = shadow_tag_rdata_q; + assign shadow_data_rdata_delayed = shadow_data_rdata_q; + + // Delay the ic_tag_rdata_i and ic_data_rdata_i inputs by 1 cycle. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + shadow_inputs_q <= delayed_inputs_t'('0); + shadow_tag_rdata_q <= '{default: 0}; + shadow_data_rdata_q <= '{default: 0}; + end else begin + shadow_inputs_q <= shadow_inputs_in; + shadow_tag_rdata_q <= ic_tag_rdata_i; + shadow_data_rdata_q <= ic_data_rdata_i; + end + end + end // Assign the inputs to the delay structure assign shadow_inputs_in.instr_gnt = instr_gnt_i; @@ -248,26 +321,6 @@ module ibex_lockstep import ibex_pkg::*; #( 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 // /////////////////// @@ -425,12 +478,12 @@ module ibex_lockstep import ibex_pkg::*; #( .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_tag_rdata_i (shadow_tag_rdata_delayed), .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_data_rdata_i (shadow_data_rdata_delayed), .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), @@ -504,8 +557,10 @@ module ibex_lockstep import ibex_pkg::*; #( logic outputs_mismatch; + // Any value except IbexMuBiOff will turn on the lockstep output comparison. 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; diff --git a/rtl/ibex_top.sv b/rtl/ibex_top.sv index 20a8822d0..a41122e48 100644 --- a/rtl/ibex_top.sv +++ b/rtl/ibex_top.sv @@ -34,6 +34,7 @@ module ibex_top import ibex_pkg::*; #( parameter bit DbgTriggerEn = 1'b0, parameter int unsigned DbgHwBreakNum = 1, parameter bit SecureIbex = 1'b0, + parameter int unsigned LockstepOffset = 1, parameter bit ICacheScramble = 1'b0, parameter int unsigned ICacheScrNumPrinceRoundsHalf = 2, parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault, @@ -1049,6 +1050,7 @@ module ibex_top import ibex_pkg::*; #( .RndCnstLfsrSeed (RndCnstLfsrSeed), .RndCnstLfsrPerm (RndCnstLfsrPerm), .SecureIbex (SecureIbex), + .LockstepOffset (LockstepOffset), .DummyInstructions(DummyInstructions), .RegFileECC (RegFileECC), .RegFileDataWidth (RegFileDataWidth), diff --git a/rtl/ibex_top_tracing.sv b/rtl/ibex_top_tracing.sv index 66e759763..d5774cea6 100644 --- a/rtl/ibex_top_tracing.sv +++ b/rtl/ibex_top_tracing.sv @@ -25,6 +25,7 @@ module ibex_top_tracing import ibex_pkg::*; #( parameter bit DbgTriggerEn = 1'b0, parameter int unsigned DbgHwBreakNum = 1, parameter bit SecureIbex = 1'b0, + parameter int unsigned LockstepOffset = 1, parameter bit ICacheScramble = 1'b0, parameter lfsr_seed_t RndCnstLfsrSeed = RndCnstLfsrSeedDefault, parameter lfsr_perm_t RndCnstLfsrPerm = RndCnstLfsrPermDefault, @@ -195,6 +196,7 @@ module ibex_top_tracing import ibex_pkg::*; #( .DbgHwBreakNum ( DbgHwBreakNum ), .WritebackStage ( WritebackStage ), .SecureIbex ( SecureIbex ), + .LockstepOffset ( LockstepOffset ), .ICacheScramble ( ICacheScramble ), .RndCnstLfsrSeed ( RndCnstLfsrSeed ), .RndCnstLfsrPerm ( RndCnstLfsrPerm ),