@@ -583,6 +583,15 @@ class chip_sw_rv_core_ibex_lockstep_glitch_vseq extends chip_sw_base_vseq;
583583 endcase
584584 end
585585
586+ // Path to the alert_major_internal signal inside the lockstep.
587+ alert_major_internal_path = $sformatf (" %s .alert_major_internal_o" , lockstep_path);
588+ alert_major_internal = 1'b0 ;
589+
590+ // An alert should be triggered, so we check for that. Depending on the glitched signal and
591+ // core it may take several clock cycles for a potential alert to fire. We wait for at most
592+ // max_delay_clks cycles.
593+ max_delay_clks = 10 + lockstep_offset;
594+
586595 // The MuBi encoded enable_cmp_q signal is responsible for enabling/disabling the
587596 // lockstep comparison. Due to the encoding and the implemented enabling/disabling
588597 // logic, we tolerate a fault into this signal. Even when faulting this signal,
@@ -602,115 +611,115 @@ class chip_sw_rv_core_ibex_lockstep_glitch_vseq extends chip_sw_base_vseq;
602611 // Force the glitched value onto the port for one cycle, then release it again.
603612 `DV_CHECK_FATAL (uvm_hdl_force (glitch_path, glitched_val));
604613 `uvm_info (`gfn , $sformatf (" Forcing %s to value 'h%0x ." , glitch_path, glitched_val), UVM_LOW )
605- if (! glitch_lockstep_core && glitched_port_is_inp) begin
606- // The input ports of the ibex_core module are defined as `logic` without an explicit net
607- // type. According to the standard, simulation tools are thus supposed to model these inputs
608- // using a `var` type. However, it turns out that some tools collapse input ports into a
609- // single object to reduce the number of assignments to improve simulation performance.
610- // As a result, glitches inserted to inputs of the non-lockstep core may propagate back and
611- // also change the input of the lockstep core. If this happens, both cores are glitched
612- // simultaneously without any alerts firing.
613- //
614- // To avoid this, we also glitch the corresponding input of the ibex_lockstep instance to
615- // the opposite value. The ibex_lockstep instance is embedded inside prim_buf cells across
616- // which glitches don't propagate back. Also, the delay lines are embedded inside the
617- // ibex_lockstep instance. It's thus fine to apply the glitch simultaneously.
618- //
619- // It's further worth noting that:
620- //
621- // 1. For some top-level inputs there may exist single points of failure, e.g. some inputs
622- // without integrity protection, some control signals without spurious enable detection.
623- // There are always such single points of failures. Where and how many there are depends
624- // on the surrounding modules, backend etc. and is out of scope for the lockstep
625- // countermeasure.
626- // 2. To verify the lockstep countermeasure, we should actually be glitching internal core
627- // signals instead of inputs to the ibex_core module. However, the problem with this is
628- // that it's infeasible to always correctly model how the glitching of any internal signal
629- // impacts core behavior and ultimately whether this should be detected. Forcing inputs
630- // of ibex_core is a way to make the test feasible in the first place.
631- //
632- // For more details refer to https://github.com/lowRISC/ibex/pull/1967 .
633- `DV_CHECK_FATAL (uvm_hdl_force (glitch_path_lockstep, orig_val));
634- `uvm_info (`gfn , $sformatf (" Forcing %s to value 'h%0x ." , glitch_path_lockstep, orig_val),
635- UVM_LOW )
636- end
637- cfg.chip_vif.cpu_clk_rst_if.wait_n_clks (1 );
638- if ((uvm_re_match (" irq_*_i" , port_name) != 0 ) && (port_name != " debug_req_i" )) begin
639- // If the port is not an interrupt or debug request, which are level-sensitive signals,
640- // release the forcing at this point.
641- `DV_CHECK_FATAL (uvm_hdl_release (glitch_path));
642- `uvm_info (`gfn , $sformatf (" Releasing force of %s ." , glitch_path), UVM_LOW )
643- if (! glitch_lockstep_core && glitched_port_is_inp) begin
644- // In case we glitched an input port of the non-lockstep core, we must now also release
645- // the force applied to the corresponding port of the ibex_lockstep instance.
646- `DV_CHECK_FATAL (uvm_hdl_release (glitch_path_lockstep));
647- `uvm_info (`gfn , $sformatf (" Releasing force of %s ." , glitch_path_lockstep), UVM_LOW )
648- end
649- end
650614
651- // An alert should be triggered, so we check for that. Depending on the glitched signal and
652- // core it may take several clock cycles for a potential alert to fire. We wait for at most
653- // max_delay_clks cycles.
654- max_delay_clks = 10 + lockstep_offset;
655-
656- // Assert that `enable_cmp_q` in `ibex_lockstep` is 1. When coming out of reset and
657- // starting execution, it takes `LockstepOffset` clock cycles for this to happen.
658- for (int i = 0 ; i < lockstep_offset; i++ ) begin
659- `DV_CHECK_FATAL (uvm_hdl_read (enable_cmp_path, enable_cmp))
660- if (enable_cmp == ibex_pkg :: IbexMuBiOn) begin
661- break ;
662- end else begin
615+ fork
616+ begin : monitor_alert_major_internal
617+ // Check that `alert_major_internal_o` of `ibex_lockstep` matches our expectation. Depending
618+ // on the glitched signal and core it may take several clock cycles for a potential alert to
619+ // fire. We wait for at most max_delay_clks cycles.
620+ for (int i = 0 ; i <= max_delay_clks; i++ ) begin
621+ `uvm_info (`gfn , $sformatf (" Checking for potential alert in cycle %0d ." , i), UVM_LOW )
622+ `DV_CHECK_FATAL (uvm_hdl_read (alert_major_internal_path, alert_major_internal))
623+ if (alert_major_internal) begin
624+ break ;
625+ end
626+ cfg.chip_vif.cpu_clk_rst_if.wait_n_clks (1 );
627+ end
628+ end : monitor_alert_major_internal
629+ begin : determine_response
630+ if (! glitch_lockstep_core && glitched_port_is_inp) begin
631+ // The input ports of the ibex_core module are defined as `logic` without an explicit net
632+ // type. According to the standard, simulation tools are thus supposed to model these inputs
633+ // using a `var` type. However, it turns out that some tools collapse input ports into a
634+ // single object to reduce the number of assignments to improve simulation performance.
635+ // As a result, glitches inserted to inputs of the non-lockstep core may propagate back and
636+ // also change the input of the lockstep core. If this happens, both cores are glitched
637+ // simultaneously without any alerts firing.
638+ //
639+ // To avoid this, we also glitch the corresponding input of the ibex_lockstep instance to
640+ // the opposite value. The ibex_lockstep instance is embedded inside prim_buf cells across
641+ // which glitches don't propagate back. Also, the delay lines are embedded inside the
642+ // ibex_lockstep instance. It's thus fine to apply the glitch simultaneously.
643+ //
644+ // It's further worth noting that:
645+ //
646+ // 1. For some top-level inputs there may exist single points of failure, e.g. some inputs
647+ // without integrity protection, some control signals without spurious enable detection.
648+ // There are always such single points of failures. Where and how many there are depends
649+ // on the surrounding modules, backend etc. and is out of scope for the lockstep
650+ // countermeasure.
651+ // 2. To verify the lockstep countermeasure, we should actually be glitching internal core
652+ // signals instead of inputs to the ibex_core module. However, the problem with this is
653+ // that it's infeasible to always correctly model how the glitching of any internal signal
654+ // impacts core behavior and ultimately whether this should be detected. Forcing inputs
655+ // of ibex_core is a way to make the test feasible in the first place.
656+ //
657+ // For more details refer to https://github.com/lowRISC/ibex/pull/1967 .
658+ `DV_CHECK_FATAL (uvm_hdl_force (glitch_path_lockstep, orig_val));
659+ `uvm_info (`gfn , $sformatf (" Forcing %s to value 'h%0x ." , glitch_path_lockstep, orig_val),
660+ UVM_LOW )
661+ end
663662 cfg.chip_vif.cpu_clk_rst_if.wait_n_clks (1 );
664- end
665- end
666- `DV_CHECK_NE_FATAL (enable_cmp, ibex_pkg :: IbexMuBiOff,
667- " Lockstep comparison disabled, which is illegal." )
663+ if ((uvm_re_match (" irq_*_i" , port_name) != 0 ) && (port_name != " debug_req_i" )) begin
664+ // If the port is not an interrupt or debug request, which are level-sensitive signals,
665+ // release the forcing at this point.
666+ `DV_CHECK_FATAL (uvm_hdl_release (glitch_path));
667+ `uvm_info (`gfn , $sformatf (" Releasing force of %s ." , glitch_path), UVM_LOW )
668+ if (! glitch_lockstep_core && glitched_port_is_inp) begin
669+ // In case we glitched an input port of the non-lockstep core, we must now also release
670+ // the force applied to the corresponding port of the ibex_lockstep instance.
671+ `DV_CHECK_FATAL (uvm_hdl_release (glitch_path_lockstep));
672+ `uvm_info (`gfn , $sformatf (" Releasing force of %s ." , glitch_path_lockstep), UVM_LOW )
673+ end
674+ end
668675
669- // Calculate whether we expect a major alert.
670- exp_alert_major_internal = 1'b0 ;
671- if (glitched_port_is_inp) begin
672- // Expect a major alert for a *used* glitched input.
673- if (glitched_inp_used) begin
674- exp_alert_major_internal = 1'b1 ;
675- `uvm_info (`gfn , " Expecting an internal major alert because glitched input is used." ,
676- UVM_LOW )
677- end else begin
676+ // An alert should be triggered, so we check for that. Depending on the glitched signal and
677+ // core it may take several clock cycles for a potential alert to fire. We wait for at most
678+ // max_delay_clks cycles.
679+ max_delay_clks = 10 + lockstep_offset;
680+
681+ // Assert that `enable_cmp_q` in `ibex_lockstep` is 1. When coming out of reset and
682+ // starting execution, it takes `LockstepOffset` clock cycles for this to happen.
683+ for (int i = 0 ; i < lockstep_offset; i++ ) begin
684+ `DV_CHECK_FATAL (uvm_hdl_read (enable_cmp_path, enable_cmp))
685+ if (enable_cmp == ibex_pkg :: IbexMuBiOn) begin
686+ break ;
687+ end else begin
688+ cfg.chip_vif.cpu_clk_rst_if.wait_n_clks (1 );
689+ end
690+ end
691+ `DV_CHECK_NE_FATAL (enable_cmp, ibex_pkg :: IbexMuBiOff,
692+ " Lockstep comparison disabled, which is illegal." )
693+
694+ // Calculate whether we expect a major alert.
678695 exp_alert_major_internal = 1'b0 ;
679- `uvm_info (`gfn , " Expecting no internal major alert because glitched input is not used." ,
680- UVM_LOW )
681- end
682- end else begin
683- // Always expect a major alert for a glitched output.
684- exp_alert_major_internal = 1'b1 ;
685- `uvm_info (`gfn , " Expecting an internal major alert due to glitched output." , UVM_LOW )
686- end
696+ if (glitched_port_is_inp) begin
697+ // Expect a major alert for a *used* glitched input.
698+ if (glitched_inp_used) begin
699+ exp_alert_major_internal = 1'b1 ;
700+ `uvm_info (`gfn , " Expecting an internal major alert because glitched input is used." ,
701+ UVM_LOW )
702+ end else begin
703+ exp_alert_major_internal = 1'b0 ;
704+ `uvm_info (`gfn , " Expecting no internal major alert because glitched input is not used." ,
705+ UVM_LOW )
706+ end
707+ end else begin
708+ // Always expect a major alert for a glitched output.
709+ exp_alert_major_internal = 1'b1 ;
710+ `uvm_info (`gfn , " Expecting an internal major alert due to glitched output." , UVM_LOW )
711+ end
712+ end : determine_response
713+ join
687714
715+ // If we expect a major internal alert, check if we have seen it.
688716 if (exp_alert_major_internal) begin
689717 seen_top_level_alert = 1'b0 ;
690718 // Give the rv_core_ibex_fatal_hw_err alert a few more cycles to propagate out compared to the
691719 // alert signal at the ibex top level.
692720 check_alert_occurs (" rv_core_ibex_fatal_hw_err" , max_delay_clks + 5 );
693721 end
694722
695- // Check that `alert_major_internal_o` of `ibex_lockstep` matches our expectation. Depending on
696- // the glitched signal and core it may take several clock cycles for a potential alert to fire.
697- // We wait for at most max_delay_clks cycles.
698- alert_major_internal_path = $sformatf (" %s .alert_major_internal_o" , lockstep_path);
699- for (int i = 0 ; i <= max_delay_clks; i++ ) begin
700- `uvm_info (`gfn , $sformatf (" Checking for potential alert in cycle %0d ." , i), UVM_MEDIUM )
701- `DV_CHECK_FATAL (uvm_hdl_read (alert_major_internal_path, alert_major_internal))
702- if (exp_alert_major_internal) begin
703- if (alert_major_internal) begin
704- `uvm_info (`gfn , $sformatf (" Major alert expectedly fired in cycle %0d ." , i), UVM_LOW )
705- break ;
706- end
707- end else begin
708- `DV_CHECK_EQ_FATAL (alert_major_internal, exp_alert_major_internal,
709- $sformatf (" Major alert unexpectedly fired in cycle %0d ." , i))
710- end
711- cfg.chip_vif.cpu_clk_rst_if.wait_n_clks (1 );
712- end
713-
714723 `DV_CHECK_EQ_FATAL (alert_major_internal, exp_alert_major_internal,
715724 " Major alert did not match expectation." )
716725
0 commit comments