Cortex-M7 DWT Watchpoint Configuration and Debug Event Issues
The Cortex-M7 processor’s Data Watchpoint and Trace (DWT) unit is a powerful tool for debugging complex issues such as race conditions, memory corruption, and unexpected behavior in embedded systems. The DWT allows developers to set watchpoints on specific memory addresses, triggering debug events when those addresses are accessed. However, configuring the DWT correctly requires a deep understanding of the Cortex-M7’s debug architecture, including the interaction between the Debug Halting Control and Status Register (DHCSR), the Debug Exception and Monitor Control Register (DEMCR), and the DWT registers. A common issue arises when the DWT is configured but fails to trigger debug events on write accesses to the monitored memory location. This problem often stems from misconfigurations in the DWT setup, improper handling of interrupts, or incorrect assumptions about the debugger’s behavior.
The DWT unit provides up to four comparators, each of which can be configured to monitor a specific memory address. When a match occurs, the DWT can generate a debug event, halting the processor and allowing the developer to inspect the system state. However, enabling the DWT and configuring its comparators is only part of the process. The Cortex-M7’s debug architecture includes several layers of control that must be properly configured to ensure that debug events are triggered as expected. These layers include the DHCSR, which controls whether the processor can be halted by debug events, and the DEMCR, which enables the DWT and other debug components. Additionally, the DWT registers themselves are protected by a lock mechanism, requiring a specific unlock sequence before they can be modified.
In the context of the provided discussion, the developer has correctly enabled the DHCSR and DEMCR registers, ensuring that the DWT is enabled and that the processor can be halted by debug events. However, the debug event is not triggered when the monitored memory location is written, indicating a potential issue with the DWT configuration or the handling of interrupts. The developer has also attempted to unlock the DWT registers by writing the value 0xC5ACCE55 to the DWT->LAR register, which is necessary to modify the DWT configuration. Despite these steps, the debug event is not triggered, suggesting that additional factors may be at play.
DWT Configuration Missteps and Interrupt Handling Oversights
One of the most common causes of DWT watchpoint failures is the improper handling of interrupts. The Cortex-M7 processor includes a global interrupt mask (PRIMASK) that controls whether interrupts can preempt the currently executing code. When PRIMASK is set to 1, interrupts are disabled, and the processor cannot be halted by external debug requests. This behavior is critical when using the DWT to monitor memory accesses, as interrupts can interfere with the timing of the debug event. If an interrupt occurs while the processor is writing to the monitored memory location, the debug event may be missed, leading to the observed behavior.
In the provided code, the developer has not explicitly disabled interrupts before writing to the monitored memory location. This omission can result in the debug event being missed if an interrupt occurs during the write operation. To address this issue, the developer should disable interrupts before writing to the monitored memory location and re-enable them afterward. This can be achieved using the __set_PRIMASK(1) and __set_PRIMASK(0) functions, which set and clear the PRIMASK bit, respectively. Alternatively, the developer can use the __disable_irq() and __enable_irq() functions, which provide a more straightforward interface for disabling and enabling interrupts.
Another potential issue is the timing of the DWT configuration. The Cortex-M7 processor includes several memory barriers (__DSB() and __ISB()) that ensure the proper ordering of memory accesses and instruction execution. These barriers are critical when configuring the DWT, as they ensure that the DWT registers are updated before the processor begins executing the code that accesses the monitored memory location. In the provided code, the developer has included __DSB() and __ISB() barriers after enabling the DWT and configuring the DWT registers. However, these barriers may not be sufficient to guarantee that the DWT is fully configured before the write operation occurs. To ensure proper timing, the developer should add additional barriers after writing to the monitored memory location and before checking the DWT->FUNCTION1 register to confirm that the debug event was triggered.
Finally, the developer should verify that the DWT comparator is configured correctly. The DWT provides four comparators, each of which can be configured to monitor a specific memory address. The comparator configuration includes the address to monitor (DWT->COMP1), the size of the memory region to monitor (DWT->MASK1), and the type of access to monitor (DWT->FUNCTION1). In the provided code, the developer has configured the comparator to monitor a 1-byte region at the address of the dummy variable and to trigger on write accesses. However, the developer should also verify that the comparator is enabled and that the DWT->FUNCTION1 register is configured correctly. The DWT->FUNCTION1 register includes a MATCHED bit that indicates whether the comparator has detected a match. If this bit is not set after the write operation, it suggests that the comparator is not configured correctly or that the debug event was not triggered.
Comprehensive DWT Watchpoint Configuration and Debug Event Verification
To resolve the issue of DWT watchpoints not triggering debug events on write accesses, the developer should follow a systematic approach to configuring the DWT and verifying its operation. This approach includes enabling the DWT, configuring the comparator, handling interrupts, and verifying the debug event. The following steps provide a detailed guide to achieving this:
Step 1: Enable the DWT and Verify Debug Halting
The first step in configuring the DWT is to ensure that the processor can be halted by debug events. This is controlled by the DHCSR register, which includes a DEBUGEN bit that must be set to 1. The developer should verify that this bit is set before proceeding with the DWT configuration. The following code demonstrates how to check the DEBUGEN bit:
uint32_t dhcsr_DEBUGEN = (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) >> CoreDebug_DHCSR_C_DEBUGEN_Pos;
assert(1 == dhcsr_DEBUGEN);
If the DEBUGEN bit is not set, the developer should ensure that the debugger is connected and that the processor is in debug mode. Once the DEBUGEN bit is verified, the developer should enable the DWT by setting the TRCENA bit in the DEMCR register. This bit enables the DWT and other trace components. The following code demonstrates how to enable the TRCENA bit:
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
__DSB();
__ISB();
The __DSB() and __ISB() barriers ensure that the TRCENA bit is set before the processor proceeds with the DWT configuration. The developer should then verify that the TRCENA bit is set:
uint32_t demcr_TRCENA = (CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk) >> CoreDebug_DEMCR_TRCENA_Pos;
assert(1 == demcr_TRCENA);
Step 2: Unlock the DWT Registers and Configure the Comparator
The DWT registers are protected by a lock mechanism, requiring a specific unlock sequence before they can be modified. The developer should write the value 0xC5ACCE55 to the DWT->LAR register to unlock the DWT registers. The following code demonstrates how to unlock the DWT registers:
DWT->LAR = 0xC5ACCE55;
Once the DWT registers are unlocked, the developer can configure the comparator. The comparator configuration includes the address to monitor (DWT->COMP1), the size of the memory region to monitor (DWT->MASK1), and the type of access to monitor (DWT->FUNCTION1). The following code demonstrates how to configure the comparator:
pdummy = &dummy;
DWT->COMP1 = (uint32_t)pdummy;
DWT->MASK1 = 1; /* 1 byte */
DWT->FUNCTION1 = 6; /* Write */
The developer should ensure that the comparator is configured correctly and that the DWT->FUNCTION1 register is set to monitor write accesses. The DWT->FUNCTION1 register includes a MATCHED bit that indicates whether the comparator has detected a match. The developer should verify this bit after the write operation to confirm that the debug event was triggered.
Step 3: Handle Interrupts and Verify the Debug Event
To ensure that the debug event is not missed due to interrupts, the developer should disable interrupts before writing to the monitored memory location and re-enable them afterward. This can be achieved using the __set_PRIMASK(1) and __set_PRIMASK(0) functions, as shown in the following code:
__set_PRIMASK(1); /* Disable interrupts */
*pdummy = 5 * ac; /* Write to the monitored memory location */
__set_PRIMASK(0); /* Re-enable interrupts */
The developer should then verify that the debug event was triggered by checking the MATCHED bit in the DWT->FUNCTION1 register:
uint32_t function_MATCHED = (DWT->FUNCTION1 & DWT_FUNCTION_MATCHED_Msk) >> DWT_FUNCTION_MATCHED_Pos;
assert(1 == function_MATCHED);
If the MATCHED bit is not set, the developer should review the DWT configuration and ensure that the comparator is configured correctly. The developer should also verify that the debugger is connected and that the processor is in debug mode.
Step 4: Additional Debugging and Verification
If the debug event is still not triggered, the developer should perform additional debugging to identify the root cause of the issue. This may include reviewing the DWT configuration, verifying the memory address being monitored, and checking for any potential hardware or software issues. The developer should also consult the Cortex-M7 Technical Reference Manual for additional information on the DWT and debug architecture.
In conclusion, configuring the DWT watchpoints on the Cortex-M7 processor requires careful attention to detail and a thorough understanding of the debug architecture. By following the steps outlined above, developers can ensure that the DWT is configured correctly and that debug events are triggered as expected. This approach will help to identify and resolve complex issues such as race conditions, memory corruption, and unexpected behavior in embedded systems.