import
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
#include <atomic.h>
|
||||
#include "cmsis.h"
|
||||
|
||||
/*
|
||||
* 注意:以下实现依赖于 CMSIS 提供的 CMSIS_LDREXW, CMSIS_STREXW, CMSIS_CLREX 等内建函数。
|
||||
* 如果使用 ARMCC,请替换为 __ldrex, __strex, __clrex。
|
||||
* 如果使用 IAR,请替换为 __LDREX, __STREX, CMSIS_CLREX。
|
||||
*/
|
||||
|
||||
/* ======================================================================== */
|
||||
/* 内部辅助宏:编译器屏障 */
|
||||
/* ======================================================================== */
|
||||
#if defined(__GNUC__)
|
||||
#define COMPILER_BARRIER() __asm volatile ("" ::: "memory")
|
||||
#elif defined(__CC_ARM)
|
||||
#define COMPILER_BARRIER() __schedule_barrier()
|
||||
#endif
|
||||
|
||||
/* ======================================================================== */
|
||||
/* 32位 原子操作实现 */
|
||||
/* ======================================================================== */
|
||||
|
||||
uint32_t atomic_load_32(const atomic_uint32_t *obj) {
|
||||
// Cortex-M3 普通加载即为原子加载(对齐情况下),但为了语义明确和防止重排
|
||||
uint32_t val;
|
||||
do {
|
||||
val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
} while (CMSIS_STREXW(val, (os_uint_t*)obj)); // 实际上 load 不需要 STREX,直接读即可,但为了严格内存序可加屏障
|
||||
// 更高效的 Load:
|
||||
return *obj;
|
||||
}
|
||||
|
||||
void atomic_store_32(atomic_uint32_t *obj, uint32_t desired) {
|
||||
// 简单存储在某些情况下可能不是原子的(如果涉及中断上下文竞争且非位带),
|
||||
// 但对于 M3,32位对齐写入是原子的。为了安全起见,使用独占序列确保完整性。
|
||||
uint32_t status;
|
||||
do {
|
||||
CMSIS_LDREXW((os_uint_t*)obj);
|
||||
status = CMSIS_STREXW(desired, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
}
|
||||
|
||||
uint32_t atomic_exchange_32(atomic_uint32_t *obj, uint32_t desired) {
|
||||
uint32_t old_val;
|
||||
uint32_t status;
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
status = CMSIS_STREXW(desired, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
return old_val;
|
||||
}
|
||||
|
||||
bool atomic_compare_exchange_strong_32(atomic_uint32_t *obj, uint32_t *expected, uint32_t desired) {
|
||||
uint32_t current;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
current = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
if (current != *expected) {
|
||||
CMSIS_CLREX(); // 清除独占标记,避免总线锁定
|
||||
*expected = current; // 更新 expected 为当前实际值
|
||||
return false;
|
||||
}
|
||||
status = CMSIS_STREXW(desired, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool atomic_compare_exchange_weak_32(atomic_uint32_t *obj, uint32_t *expected, uint32_t desired) {
|
||||
// 在 Cortex-M3 上,Weak 和 Strong 区别不大,因为硬件不支持虚假失败的优化循环通常由软件处理
|
||||
// 这里复用 Strong 逻辑,但在某些实现中 Weak 可能在第一次 STREX 失败时直接返回而不重试内部循环
|
||||
// 为了标准兼容性,我们提供标准的 CAS 行为
|
||||
return atomic_compare_exchange_strong_32(obj, expected, desired);
|
||||
}
|
||||
|
||||
uint32_t atomic_fetch_add_32(atomic_uint32_t *obj, uint32_t operand) {
|
||||
uint32_t old_val;
|
||||
uint32_t new_val;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
new_val = old_val + operand;
|
||||
status = CMSIS_STREXW(new_val, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
uint32_t atomic_fetch_sub_32(atomic_uint32_t *obj, uint32_t operand) {
|
||||
uint32_t old_val;
|
||||
uint32_t new_val;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
new_val = old_val - operand;
|
||||
status = CMSIS_STREXW(new_val, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
uint32_t atomic_fetch_and_32(atomic_uint32_t *obj, uint32_t operand) {
|
||||
uint32_t old_val;
|
||||
uint32_t new_val;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
new_val = old_val & operand;
|
||||
status = CMSIS_STREXW(new_val, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
uint32_t atomic_fetch_or_32(atomic_uint32_t *obj, uint32_t operand) {
|
||||
uint32_t old_val;
|
||||
uint32_t new_val;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
new_val = old_val | operand;
|
||||
status = CMSIS_STREXW(new_val, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
uint32_t atomic_fetch_xor_32(atomic_uint32_t *obj, uint32_t operand) {
|
||||
uint32_t old_val;
|
||||
uint32_t new_val;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_val = CMSIS_LDREXW((os_uint_t*)obj);
|
||||
new_val = old_val ^ operand;
|
||||
status = CMSIS_STREXW(new_val, (os_uint_t*)obj);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
/* ======================================================================== */
|
||||
/* 8位 原子操作实现 (基于 32位 LDREX/STREX + 掩码) */
|
||||
/* ======================================================================== */
|
||||
|
||||
uint8_t atomic_load_8(const atomic_uint8_t *obj) {
|
||||
return *obj;
|
||||
}
|
||||
|
||||
void atomic_store_8(atomic_uint8_t *obj, uint8_t desired) {
|
||||
// 需要读取包含该字节的整个32位字,修改后写回
|
||||
volatile uint32_t *word_addr = (volatile uint32_t *)((uint32_t)obj & ~0x3);
|
||||
uint32_t shift = ((uint32_t)obj & 0x3) * 8;
|
||||
uint32_t mask = 0xFF << shift;
|
||||
|
||||
uint32_t old_word;
|
||||
uint32_t new_word;
|
||||
uint32_t status;
|
||||
|
||||
do {
|
||||
old_word = CMSIS_LDREXW((os_uint_t*)word_addr);
|
||||
new_word = (old_word & ~mask) | (((uint32_t)desired << shift) & mask);
|
||||
status = CMSIS_STREXW(new_word, (os_uint_t*)word_addr);
|
||||
} while (status != 0);
|
||||
}
|
||||
|
||||
uint8_t atomic_fetch_add_8(atomic_uint8_t *obj, uint8_t operand) {
|
||||
volatile uint32_t *word_addr = (volatile uint32_t *)((uint32_t)obj & ~0x3);
|
||||
uint32_t shift = ((uint32_t)obj & 0x3) * 8;
|
||||
uint32_t mask = 0xFF << shift;
|
||||
|
||||
uint32_t old_word;
|
||||
uint32_t new_word;
|
||||
uint32_t status;
|
||||
uint8_t old_val;
|
||||
|
||||
do {
|
||||
old_word = CMSIS_LDREXW((os_uint_t*)word_addr);
|
||||
old_val = (old_word >> shift) & 0xFF;
|
||||
uint8_t new_val = old_val + operand;
|
||||
new_word = (old_word & ~mask) | (((uint32_t)new_val << shift) & mask);
|
||||
status = CMSIS_STREXW(new_word, (os_uint_t*)word_addr);
|
||||
} while (status != 0);
|
||||
|
||||
return old_val;
|
||||
}
|
||||
|
||||
bool atomic_compare_exchange_strong_8(atomic_uint8_t *obj, uint8_t *expected, uint8_t desired) {
|
||||
volatile uint32_t *word_addr = (volatile uint32_t *)((uint32_t)obj & ~0x3);
|
||||
uint32_t shift = ((uint32_t)obj & 0x3) * 8;
|
||||
uint32_t mask = 0xFF << shift;
|
||||
|
||||
uint32_t old_word;
|
||||
uint32_t new_word;
|
||||
uint32_t status;
|
||||
uint8_t current_val;
|
||||
|
||||
do {
|
||||
old_word = CMSIS_LDREXW((os_uint_t*)word_addr);
|
||||
current_val = (old_word >> shift) & 0xFF;
|
||||
|
||||
if (current_val != *expected) {
|
||||
CMSIS_CLREX();
|
||||
*expected = current_val;
|
||||
return false;
|
||||
}
|
||||
|
||||
new_word = (old_word & ~mask) | (((uint32_t)desired << shift) & mask);
|
||||
status = CMSIS_STREXW(new_word, (os_uint_t*)word_addr);
|
||||
} while (status != 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ======================================================================== */
|
||||
/* 位带操作实现 (绝对原子,无需 LDREX/STREX 循环) */
|
||||
/* ======================================================================== */
|
||||
|
||||
void atomic_bit_set(volatile void *addr, uint32_t bit) {
|
||||
// 向位带别名地址写入 1
|
||||
*BITBAND_PTR(addr, bit) = 1;
|
||||
}
|
||||
|
||||
void atomic_bit_clear(volatile void *addr, uint32_t bit) {
|
||||
// 向位带别名地址写入 0
|
||||
*BITBAND_PTR(addr, bit) = 0;
|
||||
}
|
||||
|
||||
uint32_t atomic_bit_read(volatile void *addr, uint32_t bit) {
|
||||
// 从位带别名地址读取,结果为 0 或 1
|
||||
return *BITBAND_PTR(addr, bit);
|
||||
}
|
||||
|
||||
void atomic_bit_toggle(volatile void *addr, uint32_t bit) {
|
||||
// 位带不支持直接 toggle,需要读-改-写,但因为是单比特映射,可以这样实现:
|
||||
// 读取当前位,然后写入相反值。由于读写的是不同的别名地址(如果是不同位)或同一地址,
|
||||
// 对于同一位的 toggle,最安全的方式还是 LDREX/STREX 或者关中断。
|
||||
// 但如果只是简单的 Set/Clear,位带是原子的。Toggle 通常不推荐在高位并发下使用位带,除非保证单线程访问该位。
|
||||
// 这里提供一个基于位带的简单 Toggle(注意:这在多核或极高并发中断下可能不安全,但在 M3 单核中断模型中,
|
||||
// 如果中断优先级管理得当,通常是安全的,或者更推荐使用 BSRR 寄存器进行 GPIO Toggle)
|
||||
|
||||
// 更推荐的 GPIO Toggle 是使用 BSRR:
|
||||
// 如果 addr 是 GPIO ODR,建议直接使用 BSRR 寄存器,而不是位带 Toggle。
|
||||
// 此处仅为演示位带读写能力,实际 Toggle 建议:
|
||||
uint32_t current = *BITBAND_PTR(addr, bit);
|
||||
*BITBAND_PTR(addr, bit) = !current;
|
||||
}
|
||||
Reference in New Issue
Block a user