When setting up the vector table for a Cortex-M1 processor, there are some common errors that developers can make which will cause issues. The vector table is critical for handling exceptions and interrupts properly on the Cortex-M1. Avoiding these common mistakes will ensure your Cortex-M1 application runs smoothly without unexpected crashes or hangs.
Forgetting the Initial Stack Pointer
One of the most common errors is forgetting to set the initial stack pointer in the vector table. The stack pointer register should point to the start of the stack memory region on startup. Without setting this up properly, the processor will not have a stack to use when handling exceptions and interrupts which can lead to hard faults or other issues.
To avoid this, make sure to define the stack memory region and set the initial stack pointer register in the 0th entry of the vector table to point to the start of the stack. For example: .stack : { . = ALIGN(8); _stack_start = .; . = . + STACK_SIZE; . = ALIGN(8); _stack_end = .; } .vector_table : { . = ALIGN(4); _vtable = .; VTABLE_ENTRY .stack_end // rest of vector table }
Incorrect Exception Handler Addresses
The Cortex-M1 vector table contains the addresses for all the exception handlers. Getting any of these addresses wrong will lead to problems or crashes when that particular exception occurs.
Double check that the handler functions for each exception have the correct address in the vector table. It’s easy to accidentally point to the wrong function. Testing out each exception handler individually can help catch any wrong addresses.
Not Enabling Exceptions
In order for exceptions and interrupts to be processed correctly, the appropriate enables need to be set in the Application Interrupt and Reset Control Register (AIRCR). This includes enabling the PRIMASK and FAULTMASK registers.
If these enables are not set properly, exceptions and interrupts can be ignored which can lead to unusual behavior. Make sure to read the register documentation and enable the right bits for the exceptions you need.
Faulty NVIC Configuration
The Cortex-M1 Nested Vectored Interrupt Controller (NVIC) needs to be configured properly to handle external interrupts. This includes setting up the right priority grouping and priority levels for each interrupt.
Improper priority grouping can result in interrupts being blocked incorrectly. And incorrect priority levels can cause important interrupts to be delayed for too long. Take time to plan out the NVIC priority grouping and priority levels when setting up the vector table.
Not Enabling VFP
The Cortex-M1 includes an optional floating point unit (VFP) which needs to be explicitly enabled. If the VFP is used without enabling it, the processor will crash with a usage fault exception.
Make sure to enable the VFP in the Application Interrupt and Reset Control Register if floating point operations are used. And implement the VFP exception handler to catch any floating point exceptions.
Wrong Vector Table Offset
The Cortex-M1 vector table offset register controls where in memory the processor looks for the vector table on boot. This needs to match where the vector table is located in the memory map.
If the offset is incorrect, the processor will read the wrong memory addresses for the vector table entries leading to crashes or other erratic behavior. Double check this offset matches the linker script vector table placement.
Not Handling Exceptions
While the vector table provides default exception handlers, these typically just loop indefinitely. Proper exception handlers need implemented for faults, interrupts, and system exceptions.
Without handling exceptions like memory faults, bus faults, interrupts etc., it will not be possible to recover from them and the system will hang. Implement handlers for the exceptions you need to handle in your application.
Improper Debug Configuration
To debug code via JTAG and debug probes, the Cortex-M1 debug registers need configured properly. This includes setting the right halt conditions and access permissions.
Incorrect debug configuration can make it impossible to halt the core or access memory when debugging. Consult the documentation on the Debug Exception and Monitor Control Register to enable debugging properly.
Not Saving Context on Exception
When an exception occurs, the processor state context (registers, stack, etc.) need saved by the handler if you want to return and resume where the exception occurred. The handler needs to push registers onto the stack.
Without saving the context, returning from the handler will be back at some random state leading to crashes or strange bugs. Make saving the register states part of your exception handling routines.
Bad Hard Fault Handler
The hard fault handler is called when an exceptional error condition occurs and needs robust handling. At minimum it should loop forever, but ideally would give some debug information.
Having a weak hard fault handler can result in returning from the handler to an unknown state and lead to data corruption or crashes later on. Take time to implement a robust hard fault handler.
Incorrect Linker Region Sizes
The linker script defines the memory regions for different code and data segments. Having incorrect sizes here can result in some memory being inaccessible or overwritten.
Double check the region sizes, especially the stack and heap sizes, match the required amounts for your program. Incorrect sizes will lead to weird crashes and corruption issues.
Weak Reset Handler
The processor will start executing from the reset handler on boot. This needs to perform essential startup tasks like copying data sections to RAM, zero initializing BSS, and calling main().
With a minimal reset handler, the C runtime will not initialize properly and calling main() may never occur. This would prevent the application from starting up correctly after reset.
No Bootloader Initialization
Many Cortex-M1 systems use a bootloader to perform essential system initialization like configuring clocks and memory. If using a bootloader, the reset handler needs to jump to the bootloader entry point.
Skipping the bootloader initialization will mean the clocks and memory are not configured right for the Cortex-M1 leading to crashes or freezes on reset. The reset handler should immediately pass control to the bootloader if one exists.
Using Invalid Memory Regions
The Cortex-M1 memory map will contain defined regions for RAM, peripherals, and memory-mapped I/O. Trying to access memory outside valid regions will fail.
Attempting invalid memory access will typically cause a memory management fault leading to a crash. Ensure code and data memory access is constrained to the defined valid regions.
Overwriting Flash Memory
The flash memory containing code may have limitations on writes or require erase cycles before writes. Attempting too many writes without erasing can overwrite flash memory incorrectly.
This can lead to crashes immediately or down the road as flash memory is corrupted. Understand the limitations of the flash memory hardware and ensure the code performs erase cycles when needed before writes.
Interrupt Issues
There are a couple common issues that can occur with handling interrupts in the Cortex-M1 vector table:
- Forgetting to clear the pending interrupt: Interrupts will keep firing unless the pending bit is cleared in the NVIC.
- Interrupt handler taking too long: Handlers should be short to avoid delaying other interrupts. Perform the minimum possible work in the handler.
- Accessing invalid data: Interrupt context often has limitations on what data can be accessed safely. Follow any data restrictions when accessing data in handlers.
Properly clearing interrupts, keeping handlers short, and safely accessing data will avoid a lot of subtle and difficult to debug interrupt issues.
Poor Code Optimization
Well optimized code is important for Cortex-M1 performance. The compiler can optimize for speed or size which impacts performance.
If using speed optimizations, make sure to validate any timing requirements are still met. And if using size optimizations, check that performance is still acceptable and not too degraded.
Finding the right balance between speed, size, and performance for a given application takes some trial and error. Profiling the code can help make good optimization decisions.
Invalid Compiler Flags
The compiler flags control the optimizations, defines, includes, and other build settings. Using incorrect flags can lead to subtle issues.
For example, enabling optimizations like loop unrolling without proper testing can lead to code size increasing too much. Or disabling certain optimizations can degrade performance unnecessarily.
Validate any compiler flag changes through testing before deployment. Improper compiler flags can be difficult issues to identify and debug.
Conclusion
Setting up the Cortex-M1 vector table correctly is critical for a properly functioning system. Taking the time to avoid these common errors will help prevent frustrating crashes, hangs, and bugs. Implementing solid exception handling and testing each vector table entry will help validate everything is configured right.
Robust error handling on the Cortex-M1 provides the foundation for building reliable and stable embedded and IoT applications on this versatile ARM processor.