fe310: Use read-modify-store instruction on GPIO

The rv32imac supports the A (atomic) extensions containing
read-modify-store operations. This commit modifies the GPIO code to use
these for all bitwise operations. The atomic operations are emitted with
relaxed ordering as they do not require multiprocessor synchronization.

This decreases the duration of the gpio operations from 59 ns to 50 ns
per call. depending a bit on the type of operation.
This commit is contained in:
Koen Zandberg 2020-08-29 09:24:02 +02:00
parent 029ae8c7d2
commit 889ea15936
No known key found for this signature in database
GPG Key ID: 0895A893E6D2985B

View File

@ -37,6 +37,20 @@ static gpio_flank_t isr_flank[GPIO_NUMOF];
static gpio_isr_ctx_t isr_ctx[GPIO_NUMOF];
#endif /* MODULE_PERIPH_GPIO_IRQ */
/* Really always inline these functions These two should be only a few
* instructions as the atomic_fetch_or is a single instruction on rv32imac */
static __attribute((always_inline)) inline
void _set_pin_reg(uint32_t offset, gpio_t pin)
{
__atomic_fetch_or(&GPIO_REG(offset), 1 << pin, __ATOMIC_RELAXED);
}
static __attribute((always_inline)) inline
void _clr_pin_reg(uint32_t offset, gpio_t pin)
{
__atomic_fetch_and(&GPIO_REG(offset), ~(1 << pin), __ATOMIC_RELAXED);
}
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
/* Check for valid pin */
@ -45,23 +59,24 @@ int gpio_init(gpio_t pin, gpio_mode_t mode)
}
/* Configure the mode */
switch (mode) {
case GPIO_IN:
GPIO_REG(GPIO_INPUT_EN) |= (1 << pin);
GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << pin);
GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << pin);
_set_pin_reg(GPIO_INPUT_EN, pin);
_clr_pin_reg(GPIO_OUTPUT_EN, pin);
_clr_pin_reg(GPIO_PULLUP_EN, pin);
break;
case GPIO_IN_PU:
GPIO_REG(GPIO_INPUT_EN) |= (1 << pin);
GPIO_REG(GPIO_OUTPUT_EN) &= ~(1 << pin);
GPIO_REG(GPIO_PULLUP_EN) |= (1 << pin);
_clr_pin_reg(GPIO_OUTPUT_EN, pin);
_set_pin_reg(GPIO_INPUT_EN, pin);
_set_pin_reg(GPIO_PULLUP_EN, pin);
break;
case GPIO_OUT:
GPIO_REG(GPIO_INPUT_EN) &= ~(1 << pin);
GPIO_REG(GPIO_OUTPUT_EN) |= (1 << pin);
GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << pin);
_set_pin_reg(GPIO_OUTPUT_EN, pin);
_clr_pin_reg(GPIO_INPUT_EN, pin);
_clr_pin_reg(GPIO_PULLUP_EN, pin);
break;
default:
@ -69,8 +84,8 @@ int gpio_init(gpio_t pin, gpio_mode_t mode)
}
/* Configure the pin muxing for the GPIO */
GPIO_REG(GPIO_IOF_EN) &= ~(1 << pin);
GPIO_REG(GPIO_IOF_SEL) &= ~(1 << pin);
_clr_pin_reg(GPIO_IOF_EN, pin);
_clr_pin_reg(GPIO_IOF_SEL, pin);
return 0;
}
@ -82,26 +97,26 @@ int gpio_read(gpio_t pin)
void gpio_set(gpio_t pin)
{
GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << pin);
_set_pin_reg(GPIO_OUTPUT_VAL, pin);
}
void gpio_clear(gpio_t pin)
{
GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << pin);
_clr_pin_reg(GPIO_OUTPUT_VAL, pin);
}
void gpio_toggle(gpio_t pin)
{
GPIO_REG(GPIO_OUTPUT_VAL) ^= (1 << pin);
__atomic_fetch_xor(&GPIO_REG(GPIO_OUTPUT_VAL), (1 << pin), __ATOMIC_RELAXED);
}
void gpio_write(gpio_t pin, int value)
{
if (value) {
GPIO_REG(GPIO_OUTPUT_VAL) |= (1 << pin);
_set_pin_reg(GPIO_OUTPUT_VAL, pin);
}
else {
GPIO_REG(GPIO_OUTPUT_VAL) &= ~(1 << pin);
_clr_pin_reg(GPIO_OUTPUT_VAL, pin);
}
}
@ -118,16 +133,16 @@ void gpio_isr(int num)
/* Clear interrupt */
switch (isr_flank[pin]) {
case GPIO_FALLING:
GPIO_REG(GPIO_FALL_IP) |= (1 << pin);
_set_pin_reg(GPIO_FALL_IP, pin);
break;
case GPIO_RISING:
GPIO_REG(GPIO_RISE_IP) |= (1 << pin);
_set_pin_reg(GPIO_RISE_IP, pin);
break;
case GPIO_BOTH:
GPIO_REG(GPIO_FALL_IP) |= (1 << pin);
GPIO_REG(GPIO_RISE_IP) |= (1 << pin);
_set_pin_reg(GPIO_FALL_IP, pin);
_set_pin_reg(GPIO_RISE_IP, pin);
break;
}
}
@ -172,16 +187,16 @@ void gpio_irq_enable(gpio_t pin)
/* Enable interrupt for pin */
switch (isr_flank[pin]) {
case GPIO_FALLING:
GPIO_REG(GPIO_FALL_IE) |= (1 << pin);
_set_pin_reg(GPIO_FALL_IE, pin);
break;
case GPIO_RISING:
GPIO_REG(GPIO_RISE_IE) |= (1 << pin);
_set_pin_reg(GPIO_RISE_IE, pin);
break;
case GPIO_BOTH:
GPIO_REG(GPIO_FALL_IE) |= (1 << pin);
GPIO_REG(GPIO_RISE_IE) |= (1 << pin);
_set_pin_reg(GPIO_FALL_IE, pin);
_set_pin_reg(GPIO_RISE_IE, pin);
break;
default:
@ -199,16 +214,16 @@ void gpio_irq_disable(gpio_t pin)
/* Disable interrupt for pin */
switch (isr_flank[pin]) {
case GPIO_FALLING:
GPIO_REG(GPIO_FALL_IE) &= ~(1 << pin);
_clr_pin_reg(GPIO_FALL_IE, pin);
break;
case GPIO_RISING:
GPIO_REG(GPIO_RISE_IE) &= ~(1 << pin);
_clr_pin_reg(GPIO_RISE_IE, pin);
break;
case GPIO_BOTH:
GPIO_REG(GPIO_FALL_IE) &= ~(1 << pin);
GPIO_REG(GPIO_RISE_IE) &= ~(1 << pin);
_clr_pin_reg(GPIO_FALL_IE, pin);
_clr_pin_reg(GPIO_RISE_IE, pin);
break;
default: