Files
2026-05-19 11:49:22 +08:00

253 lines
8.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}