I am trying to configure the MPU of SAMD51 (Cortex-M4) to protect one 1k of FLASH from any access, and 1k of SRAM from write access that would contains sensitive information that should never be access or corrupted (from a SW bug or any other way).
I wish to protect those sections, even in privileged (software has no RTOS and will always be in privileged mode).
As code will be running in privileged mode, I use MPU_CTRL_PRIVDEFENA_Msk flag, and wish to only overwrite access for my two regions.
I have checked the alignment requirements of those sections:
Region 0 (in FLASH) # 0x0001d400 (1k aligned)
Region 1 (in SRAM) # 0x20000000 (1k aligned)
Find below my code to configure the MPU:
__disable_irq();
//
// Region0
//
__DSB();
__ISB();
MPU->RNR = 0;
MPU->RBAR = (0x0001d400 << MPU_RBAR_ADDR_Pos);
// 1k size - log2(1k) = 10
MPU->RASR = ((10 - 1) << MPU_RASR_SIZE_Pos);
// Normal; Not shareable; Cacheable; Inner Write back; no write allocate
MPU->RASR |= ((0x4 << MPU_RASR_TEX_Pos) | (1 << MPU_RASR_C_Pos) | (1 << MPU_RASR_B_Pos));
// No priviledged or unpriviledge access
MPU->RASR |= (0UL << MPU_RASR_AP_Pos);
// Enable
MPU->RASR |= 1UL;
//
// Section 1
//
__DSB();
__ISB();
MPU->RNR = 1;
MPU->RBAR = (0x20000000 << MPU_RBAR_ADDR_Pos);
// 1k size - log2(1k) = 10
MPU->RASR = ((10 - 1) << MPU_RASR_SIZE_Pos);
// Normal; Not shareable; Cacheable; Inner Write back; no write allocate
MPU->RASR |= ((0x4 << MPU_RASR_TEX_Pos) | (1 << MPU_RASR_C_Pos) | (1 << MPU_RASR_B_Pos));
// Read-Only Priviledged or unpriviledge access
MPU->RASR |= (6UL << MPU_RASR_AP_Pos);
// Enable
MPU->RASR |= 1UL;
// Enable MPU
MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_HFNMIENA_Msk | MPU_CTRL_PRIVDEFENA_Msk;
// Enable MemFault handler
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
__DSB();
__ISB();
__enable_irq();
It appears the configuration is ignored and it never generates a fault when accessing the configured region.
I have read the ARM documentation many times and cannot see what is wrong...
Is there a mistake in my code?
Is the cache/memory configuration wrong for SAMD51?
Is what I want to do not achievable by staying in privileged mode?
Thanks for you help!
Well there are some clear problems in your code.
Please refer this for bit fields: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/BIHHGADD.html
From the start:
1: Why are you shifting the base address here
MPU->RBAR = (0x0001d400 << MPU_RBAR_ADDR_Pos);
it needs to be like this. Refer for bit fields of this register.
MPU->RBAR = (0x0001d400);
2: This line has no effect. As this field has been already set to zero while setting the size and it is also incorrect. To clear the bit we use & operation not |.
MPU->RASR |= (0UL << MPU_RASR_AP_Pos);
3: For the testing purpose try with caches off. i-e tex=1, b=0, c=0, s=0. Also set instruction fetches bit XN to disable instruction fetches access.
Related
I could find a problem related to NVIC_Init in STM32F10x library related to priorities to handle nested interrupts.
We know that any interrupt with a priority value equal or higher than BASEPRI ( 11 in our case) can call FromISR() FreeRTOS API functions.
FreeRTOS uses 15 level ( the lowest priority).
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15
In other words, FreeRTOS allows us to call API fuctions ( see xQueueSendToBackFromISR) from ISR with 15-11 priority.
When we initialize the NVIC we use level #11
#define WRTU2_DMA1_SPI2_IRQ_PRIORITY (configLIBRARY_KERNEL_INTERRUPT_PRIORITY-4*)
/* DMA1 Channel4 interrupt setting */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = WRTU2_DMA1_SPI2_IRQ_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = WRTU2_DMA1_SPI2_IRQ_PRIORITY;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
So, we should be OK. But the problem exists. I decided to check NVIC_Init.
According to the information from STM32 datasheet the priority register is 0xe000e40e ( NVIC channel 14 belongs to DMA1_Channel4 interrupts).
And I could read 0x00 from that register after NVIC was initialized. It means NVIC channel #14 has the highest priority in the system.
And it causes all problems.
I added the simplest fix NVIC->IP[DMA1_Channel4_IRQn] = 0xF0;
And the system does not fail anymore. So, our current problem is solved.
Of course, I tried to analyze what happens in NVIC_Init
**
* #brief Initializes the NVIC peripheral according to the specified
* parameters in the NVIC_InitStruct.
* #param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains
* the configuration information for the specified NVIC peripheral.
* #retval None
*/
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));
assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
{
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
/* Enable the Selected IRQ Channels --------------------------------------*/
NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
else
{
/* Disable the Selected IRQ Channels -------------------------------------*/
NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
}
So, I added the similar test code to my application to see how it converts all values
uint32_t NVIC_IRQChannelPreemptionPriority=0xFF,NVIC_IRQChannelSubPriority=0xFF;
int32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
tprintf("\n\rSCB->AIRCR=0x%08x",SCB->AIRCR);
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tprintf("\n\rtmppriority=0x%08x",tmppriority);
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tprintf("\n\rtmppre=0x%08x",tmppre);
tprintf("\n\rtmpsub=0x%08x",tmpsub);
tmppriority = (uint32_t)NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |= NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
tprintf("\n\rtmppriority=0x%08x",tmppriority);
There is a log
SCB->AIRCR=0xfa050000
tmppriority=0x00000007
tmppre=0xfffffffd
tmpsub=0x00000000
tmppriority=0x00000000
Note, even I specify 0xFF for both input parameters it returns 0x00.
I am really surprised about that behavior. This is a library function.
People use it for many years. So, I am really confused I can find find the problem in that function.
Maybe it is related to Application interrupt and reset control register (SCB_AIRCR)
Address offset: 0x0C
Reset value: 0xFA05 0000
Required privilege: Privileged
The AIRCR provides priority grouping control for the exception model, endian status for data
accesses, and reset control of the system.
Note, in the library sources I can see #define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000)
So, it looks like we have some kind of BIG vs LITTLE ENDIAN byte order in the 16-bit nibble.
Do you have any suggestion or knowledge about the problem?
I found the problem, the problem is I should use NVIC_SetPriorityGrouping(3); as 3 for the cortex-m3.
then it provides 4-bit preemptive priority and 0 bit for subpriority. At final when I initiliase like that it works as I expected.
/* DMA1 Channel4 interrupt setting */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 11;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
I am having an issue in getting my computer (virtual COM port, to be exact) to communicate with my STM32L053R8T6 (Nucleo) board by DMA and USART. Here is my code for the DMA and USART part:
#include "Device/Include/stm32l0xx.h" // Device header
#include "JB.h"
#include <string.h>
#define PCLK 32000000
#define BAUD 19200
uint8_t stringtosend[] = "test\n";
uint8_t stringtoreceive[] = " ";
void ENABLE_UART_DMA(void){
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //enable periph.clk for DMA1
/**Enabling DMA for transmission
* DMA1, Channel 4 mapped for USART2TX
* USART2 TDR for peripheral address
* stringtosend for data address
* Memory increment, memory to peripheral | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C4S) | (4 << (3 * 4));
DMA1_Channel4->CPAR = (uint32_t)&(USART2->TDR);
DMA1_Channel4->CMAR = (uint32_t)stringtosend;
DMA1_Channel4->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;
/**Enabling DMA for reception
* DMA1, Channel 5 mapped for USART2RX
* USART2 RDR for peripheral address
* stringtoreceive for data address
* Data size given
* Memory increment, peripheral to memory | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C5S) | (4 << (4 * 4));
DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
DMA1_Channel5->CMAR = (uint32_t)stringtoreceive;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);
DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN;
NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0); //NVIC enabled, max priority, channels 4-7
NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}
void CONFIGURE_UART_PARAM(void){
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
RCC->APB1ENR |= ( 1ul << 17); //Enable USART#2 clock
GPIOA->AFR[0] &= ~((15ul << 4* 3) | (15ul << 4* 2) ); //Clear PA2,PA3
GPIOA->AFR[0] |= (( 4ul << 4* 3) | ( 4ul << 4* 2) ); //Set PA2,PA3
GPIOA->MODER &= ~(( 3ul << 2* 3) | ( 3ul << 2* 2) ); //Same as above
GPIOA->MODER |= (( 2ul << 2* 3) | ( 2ul << 2* 2) );
USART2->BRR = PCLK/BAUD;
USART2->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; //Enable DMA mode in transmit and receive
/*UART enabled for transmission and reception*/
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
while((USART2->ISR & USART_ISR_TC) != USART_ISR_TC)
{
/* add time out here for a robust application */
}
USART2->ICR = USART_ICR_TCCF;
}
void CONFIGURE_EXTI(void){
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
EXTI->FTSR |= EXTI_FTSR_TR13; //falling edge trigger
EXTI->IMR |= EXTI_IMR_IM13; //unmask
NVIC_SetPriority(EXTI4_15_IRQn, 0); //def interrupt
NVIC_EnableIRQ(EXTI4_15_IRQn);
}
/*************************************************************************************************************************************************************************************************************************/
/*************************************************************************************************************************************************************************************************************************/
/*Interrupt Handlers*/
void DMA1_Channel4_5_6_7IRQHandler(void){
if((DMA1->ISR & DMA_ISR_TCIF4) == DMA_ISR_TCIF4){
DMA1->IFCR = DMA_IFCR_CTCIF4; //Clear Channel 4 Transfer Complete flag
}
else if((DMA1->ISR & DMA_ISR_TCIF5) == DMA_ISR_TCIF5){
DMA1->IFCR = DMA_IFCR_CTCIF5; //Clear Channel 5 Transfer Complete flag
DMA1_Channel5->CCR &= ~DMA_CCR_EN;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);/* Data size */
DMA1_Channel5->CCR |= DMA_CCR_EN;
}
}
void EXTI4_15_IRQHandler(void){
if(!(GPIOC->IDR & (1 << 13))){
/* Clear EXTI 13 flag */
EXTI->PR = EXTI_PR_PIF13;
/* start 8-bit transmission with DMA */
DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
}
}
//void EXTI4_15_IRQHandler(void){
// if((EXTI->PR & EXTI_PR_PIF13) == EXTI_PR_PIF13){
// /* Clear EXTI 13 flag */
// EXTI->PR = EXTI_PR_PIF13;
//
// /* start 8-bit transmission with DMA */
// DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
// DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
// DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
// }
//}
Now then, this specific code is based on an example from the STM32L0 snippets package 1.20, USART/Communcation Using DMA. USART 1 was simply redefined to USART 2 (as that is the one used by the virtual COM port), and the DMA channels were redefined according to that as well. However, the problem here is very simple: it will only print stringtosend once (would like to do it every time button B1 is pressed), and will not receive data by RX either - as if it completely ignores the DMA interrupt handler - which I am not sure how to test (no trace features available on this board). What I have seems to reflect the reference manual well enough, and all the main does is:
int main(){
SystemCoreClockInit();
CONFIGURE_UART_PARAM();
ENABLE_UART_DMA();
pushbutton_def();
CONFIGURE_EXTI();
while(1){
}
...which should just react to the defined interrupts, however it does not, and for the life of me, I cannot see why. I would love if you could help me - I would also like to avoid HAL or LL APIs - this is not a complex enough project to warrant their usage (several inputs, outputs, comms between two boards by USART/DMA), plus I would prefer to learn working closer to the register level.
Thanks!
edit (in response to Berendi's suggestions):
1. GPIOC was defined in another file, called with pushbutton_def():
RCC->IOPENR |= (1UL << 2); //enable GPIOC
I understand exactly what you mean by your explanation (indeed, the register referred by those two is "the same", 0x00000020U), but I am not sure as to how to redefine it: here is my attempt after looking at the reference manual (SYSCFG part) and the source (still, it does not work):
SYSCFG->EXTICR[3] = ((SYSCFG->EXTICR[3] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC);
As suggested, I have added USART2->ICR = USART_ICR_TCCF; to the EXTIhandler, right after the DMA channels. I have kept it in the USART definition. The message is still only being sent once, though.
GPIOC is not enabled
Here,
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
you should enable GPIOC too.
EXTI13 is mapped to PA13
Here,
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
you are setting the configuration register for EXTI0-EXTI3, actually mapping EXTI1 to PC1. EXTI13 remains mapped to PA13, which is actually SWDIO, connected to the onboard debugger. I guess the traffic on SWDIO triggers the EXTI interrupt, the handler checks PC13 which is always reading 0 because the port is disabled, and enables DMA. DMA transmit works only once though, because
USART_ISR_TC is not cleared in the interrupt
but only once at startup. You should move this line
USART2->ICR = USART_ICR_TCCF;
to the EXTI interrupt handler.
I'm not sure why receiving doesn't work, perhaps the DMA handler has no chance to run, because EXTI is constantly retriggered by SWD traffic. Both interrupts have the same priority, the one with lower interrupt number wins, which is the EXTI handler. If it's always retriggered before it finishes, then it will be called again, not letting the other handler to run.
I wrote a simple program to generate PWM wave with 50% duty cycle. Then I went for debugging in AtmelStudio. All registers except OCR0 were assigned there respective values. Why OCR0 not assigned any value.
ATmega32, Fast PWM.
#include <avr/io.h>
int main(void)
{
DDRB = (1 << PB3);
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);
OCR0 = 127;
TCCR0 |= (1 << CS02);
return 0;
}
So anyway.
You're using the 8-bit counter0 on your Atmega32. Let's see how you set it up:
// Set Pin B3 as output, others as input
DDRB = (1 << PB3);
// Set Clear on compare match + Fast PWM mode + Counter stopped
TCCR0 |= (1 << WGM01) | (1 << WGM00) | (1 << COM01);
// Set comparator value to 127
OCR0 = 127;
// Enable clkIO/256 from prescaler, turning on the counter
TCCR0 |= (1 << CS02);
Okay. First, a few things:
On initial setup, you usually want to assign the value and not or it, to be certain of its state.
Even after, setting it instead of or-ing it avoids a useless read. No impact on behavior for this register, but might be a bit better for performance.
The documentation recommends only enabling the output after you have set it up properly, to avoid spurious output. So you should move the first line last.
I'll be reading from that version of the datasheet.
Now, in fast PWM mode, according to table 38, and 40, the counter behaves like this:
It counts from BOTTOM to MAX (0 to 0xFF).
OCR0 is only used to toggle OC0 pin, not to reset the counting.
OCR0 has double-buffering. Its actual value is not updated until next cycle.
This might be your issue. If any of those are true:
Counter doesn't start properly (could happen if CS2-0 are not correct due to or-ing them instead of setting them).
Counter is stopped early (because your program ends and if the studio detects it, it could disable it at that point - I d'ont use the studio so I cannot really tell).
Then it is possible the value you write to the double buffer never makes it to the actual register. Alas the datasheet doesn't explain in detail how this is handled. Nor does it tell whether reading OCR0 while double buffering is active returns current value or awaiting value.
I'm using an MSP430 Launchpad. To be more specific I'm using the microcontroler MS430G2553. I was trying to compile some code designed for the MS430G2230 but the problem is that some parts of the code wont match the MS430G2553.
This is the code
void USI_Init (void)
{
// configure SPI
USICTL0 |= USISWRST; // USI in reset
USICTL0 = USICTL0 | USILSB; // Least Significant Bit first
USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIMST + USIOE; // Port, SPI Master
USICTL1 |= USICKPH; // flag remains set
USICKCTL = USIDIV_1 + USISSEL_2; // /2 SMCLK
USICTL0 &= ~USISWRST; // USI released for operation
USICNT = USICNT | 0x50; // 16 bit mode; 16 bit to be transmitted/received
return;
}
and this is the second routine that doesn't work
#pragma vector=WDT_VECTOR
__interrupt void Write_Matrix(void)
{
static unsigned char index=0;
P1OUT |= DATA_LATCH_PIN;
P1OUT &= ~DATA_LATCH_PIN;
USICTL1 &= ~USIIFG; // Clears the interrupt flag
USISRH = 1<<index; // Move the index of the column in the high bits of USISR
USISRL = Matrix[index]; // Move the index of the rows (value of Matrix[index]) in the low bits of USIRS
USICNT = USICNT | 0x10; // 16 bit format
index = (index+1) & 7;
return;
}
Any Ideas?
Thanks
First off, you shouldn't expect to have code that is 100% portable between those two families of processors. The MSP430G2553 is a much larger value line processor and comes with more peripherals than the MSP430G2230.
Please refer to the following diagrams:
MSP430G2230 Functional Diagram
MSP430G2553 Functional Diagram
As you can see, these MCUs are very different.
Your first routine doesn't work because the MSP430G2553 doesn't have a USI peripheral. Instead, SPI communication is performed using a USCI peripheral. You will need to modify your code to use this peripheral instead. Please reference the User's Guide for more information.
Your second routine doesn't work because of the lack of USI peripheral again. Notice the references to USI registers: USICTL1 &= ~USIIFG;, etc. You will need to once again modify your code to use the USCI peripheral.
I want to use the Cortex-M3 MPU to protect the memory from unintended access. For this purpose I configured the MPU. But I did not get any exception at all. Other exceptions as BusFault, HardFault has been forced with appropriate "bad code".
The current initialization is
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; // disable MPU
MPU->RNR = 0
MPU->RBAR = 0x20000000;
MPU->RASR = 0x03000019; // 0x03UL << (MPU_RASR_ATTRS_Pos+8) | (0x19UL << 0);
MPU->RNR = 1;
MPU->RBAR = 0x8000000; // read back as 0x8000001
MPU->RASR = 0x03000027; // 0x03UL << (MPU_RASR_ATTRS_Pos+8) | (0x27UL << 0);
MPU->RNR = 2;
MPU->RBAR = 0x40000000; // read back as 0x40000002
MPU->RASR = 0x13000039; // 0x13UL << (MPU_RASR_ATTRS_Pos+8) | (0x39UL << 0);
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; // Enable the memory fault exception
MPU->CTRL |= MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; // Enable MPU
Until now I can access all memory. Now I add a region to protect a region.
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk; // disable MPU
MPU->RNR = 3;
MPU->RBAR = 0x20002000; // read back as 0x20002003
MPU->RASR = 0x00000009; // 0x00UL << (MPU_RASR_ATTRS_Pos+8) | (0x09UL << 0);
MPU->CTRL |= MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; // re-enable MPU
The region number 3 should hide all other MPU regions. Now the memory at 0x20002000 should not be accessible neither readable not writable. But I can still read and even write the memory at this address.
What can inhibit the memory protection in a NXP LPC1788 CPU?