@@ -113,6 +113,7 @@ class CortexM(Target, CoreSightCoreComponent):
113113 C_STEP = (1 << 2 )
114114 C_MASKINTS = (1 << 3 )
115115 C_SNAPSTALL = (1 << 5 )
116+ C_PMOV = (1 << 6 )
116117 S_REGRDY = (1 << 16 )
117118 S_HALT = (1 << 17 )
118119 S_SLEEP = (1 << 18 )
@@ -473,68 +474,118 @@ def halt(self):
473474 self .flush ()
474475 self .session .notify (Target .Event .POST_HALT , self , Target .HaltReason .USER )
475476
476- def step (self , disable_interrupts = True , start = 0 , end = 0 ):
477+ def step (self , disable_interrupts = True , start = 0 , end = 0 , hook_cb = None ):
477478 """! @brief Perform an instruction level step.
478479
479- This function preserves the previous interrupt mask state.
480+ This API will execute one or more individual instructions on the core. With default parameters, it
481+ masks interrupts and only steps a single instruction. The _start_ and _stop_ parameters define an
482+ address range of [_start_, _end_). The core will be repeatedly stepped until the PC falls outside this
483+ range, a debug event occurs, or the optional callback returns True.
484+
485+ The _disable_interrupts_ parameter controls whether to allow stepping into interrupts. This function
486+ preserves the previous interrupt mask state.
487+
488+ If the _hook_cb_ parameter is set to a callable, it will be invoked repeatedly to give the caller a
489+ chance to check for interrupt requests or other reasons to exit.
490+
491+ Note that stepping may take a very long time for to return in cases such as stepping over a branch
492+ into the Secure world where the debugger doesn't have secure debug access, or similar for Privileged
493+ code in the case of UDE.
494+
495+ @param self The object.
496+ @param disable_interrupts Boolean specifying whether to mask interrupts during the step.
497+ @param start Integer start address for range stepping. Not included in the range.
498+ @param end Integer end address for range stepping. The range is inclusive of this address.
499+ @param hook_cb Optional callable taking no parameters and returning a boolean. The signature is
500+ `hook_cb() -> bool`. Invoked repeatedly while waiting for step operations to complete. If the
501+ callback returns True, then stepping is stopped immediately.
502+
503+ @exception DebugError Raised if debug is not enabled on the core.
480504 """
481- # Was 'if self.get_state() != TARGET_HALTED:'
482- # but now value of dhcsr is saved
483- dhcsr = self .read_memory (CortexM .DHCSR )
484- if not (dhcsr & (CortexM .C_STEP | CortexM .C_HALT )):
485- LOG .error ('cannot step: target not halted' )
505+ # Save DHCSR and make sure the core is halted. We also check that C_DEBUGEN is set because if it's
506+ # not, then C_HALT is UNKNOWN.
507+ dhcsr = self .read32 (CortexM .DHCSR )
508+ if not (dhcsr & CortexM .C_DEBUGEN ):
509+ raise exception .DebugError ('cannot step: debug not enabled' )
510+ if not (dhcsr & CortexM .C_HALT ):
511+ LOG .error ('cannot step: core not halted' )
486512 return
487513
488- LOG .debug ("step core %d" , self .core_number )
514+ if start != end :
515+ LOG .debug ("step core %d (start=%#010x, end=%#010x)" , self .core_number , start , end )
516+ else :
517+ LOG .debug ("step core %d" , self .core_number )
489518
490519 self .session .notify (Target .Event .PRE_RUN , self , Target .RunType .STEP )
491520
521+ self ._run_token += 1
522+
492523 self .clear_debug_cause_bits ()
493524
494- # Save previous interrupt mask state
495- interrupts_masked = (CortexM .C_MASKINTS & dhcsr ) != 0
525+ # Get current state.
526+ saved_maskints = dhcsr & CortexM .C_MASKINTS
527+ saved_pmov = dhcsr & CortexM .C_PMOV
528+ maskints_differs = bool (saved_maskints ) != disable_interrupts
496529
497- # Mask interrupts - C_HALT must be set when changing to C_MASKINTS
498- if not interrupts_masked and disable_interrupts :
499- self .write_memory (CortexM .DHCSR , CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_HALT | CortexM .C_MASKINTS )
530+ # Get the DHCSR value to use when stepping based on whether we're masking interrupts.
531+ dhcsr_step = CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_STEP | saved_pmov
532+ if disable_interrupts :
533+ dhcsr_step |= CortexM .C_MASKINTS
500534
501- # Single step using current C_MASKINTS setting
502- while True :
503- if disable_interrupts or interrupts_masked :
504- self .write_memory (CortexM .DHCSR , CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_MASKINTS | CortexM .C_STEP )
505- else :
506- self .write_memory (CortexM .DHCSR , CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_STEP )
535+ # Update mask interrupts setting - C_HALT must be set when changing to C_MASKINTS.
536+ if maskints_differs :
537+ self .write32 (CortexM .DHCSR , dhcsr_step | CortexM .C_HALT )
507538
508- # Wait for halt to auto set (This should be done before the first read)
509- while not self .read_memory (CortexM .DHCSR ) & CortexM .C_HALT :
510- pass
539+ # Get the step timeout. A timeout of 0 means no timeout, so we have to pass None to the Timeout class.
540+ step_timeout = self .session .options .get ('cpu.step.instruction.timeout' ) or None
541+
542+ while True :
543+ # Single step using current C_MASKINTS setting
544+ self .write32 (CortexM .DHCSR , dhcsr_step )
545+
546+ # Wait for halt to auto set.
547+ #
548+ # Note that it may take a very long time for this loop to exit in cases such as stepping over
549+ # a branch into the Secure world where the debugger doesn't have secure debug access, or similar
550+ # for Privileged code in the case of UDE.
551+ with timeout .Timeout (step_timeout ) as tmo :
552+ while tmo .check ():
553+ if (self .read32 (CortexM .DHCSR ) & CortexM .C_HALT ) != 0 :
554+ break
555+ # Invoke the callback if provided. If it returns True, then exit the loop.
556+ if (hook_cb is not None ) and hook_cb ():
557+ break
511558
512559 # Range is empty, 'range step' will degenerate to 'step'
513560 if start == end :
514561 break
515562
516563 # Read program counter and compare to [start, end)
517564 program_counter = self .read_core_register ('pc' )
518- if program_counter < start or end <= program_counter :
565+ if ( program_counter < start ) or ( end <= program_counter ) :
519566 break
520567
521- # Check other stop reasons
522- if self .read_memory (CortexM .DFSR ) & ( CortexM .DFSR_DWTTRAP | CortexM . DFSR_BKPT ) :
568+ # Check for stop reasons other than HALTED, which will have been set by our step action.
569+ if ( self .read32 (CortexM .DFSR ) & ~ CortexM .DFSR_HALTED ) != 0 :
523570 break
524571
525- # Restore interrupt mask state
526- if not interrupts_masked and disable_interrupts :
527- # Unmask interrupts - C_HALT must be set when changing to C_MASKINTS
528- self . write_memory ( CortexM . DHCSR , CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_HALT )
572+ # Restore interrupt mask state.
573+ if maskints_differs :
574+ self . write32 ( CortexM . DHCSR ,
575+ CortexM .DBGKEY | CortexM .C_DEBUGEN | CortexM .C_HALT | saved_maskints | saved_pmov )
529576
530577 self .flush ()
531578
532- self ._run_token += 1
533-
534579 self .session .notify (Target .Event .POST_RUN , self , Target .RunType .STEP )
535580
536581 def clear_debug_cause_bits (self ):
537- self .write_memory (CortexM .DFSR , CortexM .DFSR_VCATCH | CortexM .DFSR_DWTTRAP | CortexM .DFSR_BKPT | CortexM .DFSR_HALTED )
582+ self .write32 (CortexM .DFSR ,
583+ CortexM .DFSR_EXTERNAL
584+ | CortexM .DFSR_VCATCH
585+ | CortexM .DFSR_DWTTRAP
586+ | CortexM .DFSR_BKPT
587+ | CortexM .DFSR_HALTED
588+ )
538589
539590 def _perform_emulated_reset (self ):
540591 """! @brief Emulate a software reset by writing registers.
0 commit comments