Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STM32 SPI hardware and strict aliasing warnings

Tags:

I've seen that the subject has been discussed in many other questions, but I can't quite find the answer for my particular case.

I am working with a STM32F0 microcontroller. The top of the SPI reception/transmit FIFO are accessible by a memory access. This particular microcontroller allows me to read/write 8bits or 16bits from the top of the FIFO. More precisely, when a LDRB/STRB instruction is executed, 8bits are popped/pushed from/to the FIFO and when a LDRH/STRH instruction is executed, 16 bits are popped/pushed from/to the FIFO.

The Hardware Abstraction Layer provided by STMicroelectronic proposes this syntax to read the SPI FIFO.

return *(volatile uint8_t*)&_handle->Instance->DR; // Pop 1 byte
return *(volatile uint16_t*)&_handle->Instance->DR; // Pop 2 byte

*(volatile uint8_t*)&_handle->Instance->DR = val; // Push 1 byte
*(volatile uint16_t*)&_handle->Instance->DR = val; // Push 2 bytes

Where DR is a uint32_t* pointing on the top of the SPI FIFO

I've built my software using this syntax and it does work fine. The only problem, is that g++ throws a lot of warning about type punning. More precisely:

Inc/drivers/SPI.h:70:50: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] return *(volatile uint16_t*)&_handle->Instance->DR;

After some readings it looks like using union is not a good idea in C++. I did try it anyway but got some problems. Actually accessing the memory through a pointer in a union makes my microcontroller crashes just like an unaligned memory access.

static_cast and reinterpret_cast throws the sames warning as the C-style cast

I cannot use memcpy with void* since my final goal is to make the compiler uses a LDRB/STRB and LDRH/STRH instruction.

Others proposed solutions that I found on Stack Overflow were dependent on the use-case.

Any suggestion?

like image 610
Pier-Yves Lessard Avatar asked Oct 11 '16 04:10

Pier-Yves Lessard


1 Answers

I would suggest creating two specific pointers for the job. You can create them during initialisation or statically so you don't have to create them each time.

static uint8_t * const DR_Byte = (uint8_t * const)&_handle->Instance->DR;
static uint16_t * const DR_Word = (uint16_t * const)&_handle->Instance->DR;

then simply read by:

uint8_t read_byte = *DR_Byte;
uint16_t read_word = *DR_Word;

and write by:

*DR_Byte = byte_to_write;
*DR_Word = word_to_write;

or something similar.

like image 176
Realtime Rik Avatar answered Sep 24 '22 16:09

Realtime Rik