This driver for Atmel® | SMART ARM®-based microcontroller provides an interface for the locking and unlocking of peripheral registers within the device.
When a peripheral is locked, accidental writes to the peripheral will be blocked and a CPU exception will be raised.
The following peripherals are used by this module:
The following devices can use this module:
The outline of this documentation is as follows:
There are no prerequisites for this module.
The SAM devices are fitted with a Peripheral Access Controller (PAC) that can be used to lock and unlock write access to a peripheral's registers (see Non-Writable Registers). Locking a peripheral minimizes the risk of unintended configuration changes to a peripheral as a consequence of Run-away Code or use of a Faulty Module Pointer.
Physically, the PAC restricts write access through the AHB bus to registers used by the peripheral, making the register non-writable. PAC locking of modules should be implemented in configuration critical applications where avoiding unintended peripheral configuration changes are to be regarded in the highest of priorities.
All interrupt must be disabled while a peripheral is unlocked to make sure correct lock/unlock scheme is upheld.
The module has a built in safety feature requiring that an already locked peripheral is not relocked, and that already unlocked peripherals are not unlocked again. Attempting to unlock and already unlocked peripheral, or attempting to lock a peripheral that is currently locked will generate a CPU exception. This implies that the implementer must keep strict control over the peripheral's lock-state before modifying them. With this added safety, the probability of stopping runaway code increases as the program pointer can be caught inside the exception handler, and necessary countermeasures can be initiated. The implementer should also consider using sanity checks after an unlock has been performed to further increase the security.
A recommended implementation of the PAC can be seen in the figure below.
Global interrupts must be disabled while a peripheral is unlocked as an interrupt handler would not know the current state of the peripheral lock. If the interrupt tries to alter the lock state, it can cause an exception as it potentially tries to unlock an already unlocked peripheral. Reading current lock state is to be avoided as it removes the security provided by the PAC (Reading Lock State).
An example to illustrate the potential hazard of not disabling interrupts is shown in the diagram below.
Run-away code can be caused by the MCU being operated outside its specification, faulty code, or EMI issues. If a runaway code occurs, it is favorable to catch the issue as soon as possible. With a correct implementation of the PAC, the runaway code can potentially be stopped.
A graphical example showing how a PAC implementation will behave for different circumstances of runaway code in shown in the first and second figures below.
In the example, green indicates that the command is allowed, red indicates where the runaway code will be caught, and the arrow where the runaway code enters the application. In special circumstances, like example 4 above, the runaway code will not be caught. However, the protection scheme will greatly enhance peripheral configuration security from being affected by runaway code.
To protect the module functions against runaway code themselves, a key is required as one of the input arguments. The key-argument will make sure that runaway code entering the function without a function call will be rejected before inflicting any damage. The argument is simply set to be the bitwise inverse of the module flag, i.e.
Where the lock state can be either lock or unlock, and module refer to the peripheral that is to be locked/unlocked.
The PAC also protects the application from user errors such as the use of incorrect module pointers in function arguments, given that the module is locked. It is therefore recommended that any unused peripheral is locked during application initialization.
Using the function attribute __no_inline
will ensure that there will only be one copy of each functions in the PAC driver API in the application. This will lower the likelihood that runaway code will hit any of these functions.
The diagram below shows how this module is interconnected within the device.
Not all registers in a given peripheral can be set non-writable. Which registers this applies to is showed in List of Non-Write Protected Registers and the peripheral's subsection "Register Access Protection" in the device datasheet.
Reading the state of the peripheral lock is to be avoided as it greatly compromises the protection initially provided by the PAC. If a lock/unlock is implemented conditionally, there is a risk that eventual errors are not caught in the protection scheme. Examples indicating the issue are shown in the diagram below.
In the left figure above, one can see the runaway code continues as all illegal operations are conditional. On the right side figure, the runaway code is caught as it tries to unlock the peripheral.
For extra information, see Extra Information for PAC Driver. This includes:
For a list of examples related to this driver, see Examples for PAC Driver.
Macros | |
#define | SYSTEM_PERIPHERAL_ID(peripheral) ID_##peripheral |
Retrieves the ID of a specified peripheral name, giving its peripheral bus location. More... | |
Peripheral Lock and Unlock | |
__no_inline enum status_code | system_peripheral_lock (const uint32_t peripheral_id, const uint32_t key) |
Lock a given peripheral's control registers. More... | |
__no_inline enum status_code | system_peripheral_unlock (const uint32_t peripheral_id, const uint32_t key) |
Unlock a given peripheral's control registers. More... | |
APIs available for SAM L21/L22/C20/C21. | |
__no_inline enum status_code | system_peripheral_lock_always (const uint32_t peripheral_id, const uint32_t key) |
Lock a given peripheral's control registers until hardware reset. More... | |
static void | system_pac_enable_interrupt (void) |
Enable PAC interrupt. More... | |
static void | system_pac_disable_interrupt (void) |
Disable PAC interrupt. More... | |
static void | system_pac_enable_event (void) |
Enable PAC event output. More... | |
static void | system_pac_disable_event (void) |
Disable PAC event output. More... | |
#define SYSTEM_PERIPHERAL_ID | ( | peripheral | ) | ID_##peripheral |
Retrieves the ID of a specified peripheral name, giving its peripheral bus location.
[in] | peripheral | Name of the peripheral instance |
Referenced by dsu_crc32_cal(), and main().
|
inlinestatic |
Disable PAC event output.
Disable PAC event output on peripheral access error.
|
inlinestatic |
Disable PAC interrupt.
Disable PAC interrupt on peripheral access error.
|
inlinestatic |
Enable PAC event output.
Enable PAC event output on peripheral access error.
|
inlinestatic |
Enable PAC interrupt.
Enable PAC interrupt so can trigger execution on peripheral access error, see SYSTEM_Handler().
Referenced by main().
__no_inline enum status_code system_peripheral_lock | ( | const uint32_t | peripheral_id, |
const uint32_t | key | ||
) |
Lock a given peripheral's control registers.
Locks a given peripheral's control registers, to deny write access to the peripheral to prevent accidental changes to the module's configuration.
[in] | peripheral_id | ID for the peripheral to be locked, sourced via the SYSTEM_PERIPHERAL_ID macro |
[in] | key | Bitwise inverse of peripheral ID, used as key to reduce the chance of accidental locking. See Key-Argument |
STATUS_OK | If the peripheral was successfully locked |
STATUS_ERR_INVALID_ARG | If invalid argument(s) were supplied |
References Assert, STATUS_ERR_INVALID_ARG, and STATUS_OK.
Referenced by dsu_crc32_cal(), and main().
__no_inline enum status_code system_peripheral_lock_always | ( | const uint32_t | peripheral_id, |
const uint32_t | key | ||
) |
Lock a given peripheral's control registers until hardware reset.
Locks a given peripheral's control registers, to deny write access to the peripheral to prevent accidental changes to the module's configuration. After lock, the only way to unlock is hardware reset.
[in] | peripheral_id | ID for the peripheral to be locked, sourced via the SYSTEM_PERIPHERAL_ID macro |
[in] | key | Bitwise inverse of peripheral ID, used as key to reduce the chance of accidental locking. See Key-Argument |
STATUS_OK | If the peripheral was successfully locked |
STATUS_ERR_INVALID_ARG | If invalid argument(s) were supplied |
References Assert, STATUS_ERR_INVALID_ARG, and STATUS_OK.
__no_inline enum status_code system_peripheral_unlock | ( | const uint32_t | peripheral_id, |
const uint32_t | key | ||
) |
Unlock a given peripheral's control registers.
Unlocks a given peripheral's control registers, allowing write access to the peripheral so that changes can be made to the module's configuration.
[in] | peripheral_id | ID for the peripheral to be unlocked, sourced via the SYSTEM_PERIPHERAL_ID macro |
[in] | key | Bitwise inverse of peripheral ID, used as key to reduce the chance of accidental unlocking. See Key-Argument |
STATUS_OK | If the peripheral was successfully locked |
STATUS_ERR_INVALID_ARG | If invalid argument(s) were supplied |
References Assert, STATUS_ERR_INVALID_ARG, and STATUS_OK.
Referenced by dsu_crc32_cal(), and main().