2017-10-23 12:36 PM
I'm looking at file stm8l10x_gpio.c from the 'standard peripheral library'
and there does not seem to be a function to switch individual GPIO bits atomically.
For example the following compiles with Cosmic compiler to read-modify-write sequence
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint8_t GPIO_Pin)
{ GPIOx->ODR |= GPIO_Pin;}as well as this:
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin, BitAction GPIO_BitVal)
{ if (GPIO_BitVal != RESET) { SetBit(GPIOx->ODR, GPIO_Pin);}
else { ClrBit(GPIOx->ODR, GPIO_Pin); }}So when interrupt handler drives some GPIO pin and the main program/other ISR uses other pin in same GPIO register, locking is needed. But ST8 has special instructions for manipulating single bits.
Macros SetBit, ClrBit are defined in stm8l10x.h and neither these are atomic.
Are there any intrinsic functions for bit set/clear (Cosmic compiler if possible)?
Thanks,
-- pa
2017-10-23 01:47 PM
I would suggest you refer to the STM8 processor documentation and make a determination if it supports such things architecturally. The GPIO doesn't have the BSRR type register of the STM32
2017-10-23 05:04 PM
Yeah STM8 supports bit instructions architecturally. Just like 8051.
But in C code this needs compiler-specific quirks. For Cosmic compiler, this looks like:
_Bool fire_missile @ GPIOA_ODR:1 ; // GPIO A bit 0x02
_Bool blink_led
@ GPIOA_ODR:2 ; // GPIO A bit 0x04
void interrupt_handler() {
fire
_missile = 1;
}
void main() {
while(1) {... blink_led = 1; .... blink_led = 0; ... }
}
The _Bool's are translated to native ST8 bit variables and assignments are single bit set/clear instructions that affect only one bit. No locking is needed on the
GPIOA_ODR register as in case of
GPIOA_ODR |= 0x02.
If only I understand correctly, byte 'or' instruction has only one variant where destination is reg. A. No variant exists with destination in memory.
My question is, what people use to achieve lockless access to GPIO outputs? Disable interrupts across 'standard library' calls, or roll their own?
Regards,
- p
2017-10-23 07:22 PM
As long as the IO port is declared as volatile and the compiler optimisation is properly setup, the compiler should generate the proper code, lock mecanism included.
As far as I remember, there is a bitset/bitreset read/modify/write instruction for a limited memory range.
2017-10-24 08:46 AM
Well, this is disassembly of stm8l10x_gpio.c
void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint8_t GPIO_Pin)
{ GPIOx->ODR |= GPIO_Pin;}stm8l10x_gpio.c:220 GPIOx->ODR |= GPIO_Pin; LD A,(X) <--------------1 OR A,(0x03,SP) LD (X),A <------------- 2RETAs you see, no locking. If interrupt changes the GPIO data register (the address is in X) between points 1 and 2, the program is screwed. At least, worth to be documented.
Regards,
- p.
2017-10-25 12:47 AM
Hello,
from the compiler point of view (Cosmic) bit accesses are converted to single BSET and BRES instructions whenever this is possible, but nothing is done automatically to ensure atomic access (the application code must take care of that)
In my memory the SetBit and ClearBit macros were converted to single instructions, but if you say this is not the case I'll check and report back here.
Regards,
Luca
2017-10-25 09:04 AM
The stm8 bit set / bres / bcpl instructions exist only for direct addressing.
I would assume, that Cosmic, like SDCC uses them for setting / resetting / inverting bits in global variables (including I/O). But the posted code example sets bits through a pointer, so unless the function gets inlined and the pointer access somehow optimized out, compilers won't be able to use an STM8 bit set instruction.
Philipp
2017-10-25 04:00 PM
One can write a BSET/BRES instruction on the fly. It's not the fastest way to do it, and it's ugly, but it works.
2017-10-25 04:40 PM
Inlining looks OK for me. Usually the GPIO pins are referred directly.
Regarding the
SetBit and ClearBit macros - the .h file has alternative macros BitSet and BitClear - but I don't understand what they do. These seem to belong to some other architecture that has a limited area for bits, like 8051.
Inline implem. for Cosmic compiler can be like following:
typedef struct {
_Bool bit0;
_Bool bit1; _Bool bit2;
_Bool bit3; _Bool bit4;
_Bool bit5; _Bool bit6;
_Bool bit7;
} bytebits_t;
@inline void bset( volatile bytebits_t *pb, char i ) {
switch(i) {
case 0: pb->bit0 = 1; break;
case 1: pb->bit1 = 1; break;
case 2: pb->bit2 = 1; break;
case 3: pb->bit3 = 1; break;
case 4: pb->bit4 = 1; break;
case 5: pb->bit5 = 1; break;
case 6: pb->bit6 = 1; break;
case 7: pb->bit7 = 1; break;
}
}
@inline void bres( volatile bytebits_t *pb, char i ) {
switch(i) {
case 0: pb->bit0 = 0; break;
............... etc.
@inline void GPIO_WriteBit(GPIO_TypeDef* GPIOx, GPIO_Pin_TypeDef GPIO_Pin, BitAction GPIO_BitVal)
{if (GPIO_BitVal != RESET){ bset(&GPIOx->ODR, GPIO_Pin);}
else{ bres(&GPIOx->ODR, GPIO_Pin);}}Creating BSET/BRES instructions on the fly also may work, as STM8 can execute code from RAM.
--p
2017-10-26 01:37 AM
While the STM8 supports code execution from RAM, and thus self-modifying code, I wouldn't expect any compiler to use it here. It would be slow and complicated. Since the language doesn't require it, I wouldn't want compilers to generate slow code for such basic operations as setting a bit through a pointer. Not even with volatile.
Maybe a compiler could use self-modifying code to implement the ISO C11 atomic_fetch_or() and similar functions. But atomics are optional in ISO C11. Currently the only compiler that supports ISO C11 for the STM8 is SDCC, and SDCC defines __STDC_NO_ATOMICS__, and does not support atomics.
Philipp