Skip to content

Commit c044785

Browse files
committed
cortex-m: Add atomic helpers. Use in nrf time hal
1 parent ef7d70f commit c044785

File tree

2 files changed

+86
-4
lines changed

2 files changed

+86
-4
lines changed

core/src/cpus/cortex_m.zig

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,88 @@ pub fn clrex() void {
538538
asm volatile ("clrex");
539539
}
540540

541+
/// Atomic operations with fallback to critical sections for Cortex-M0/M0+
542+
pub const atomic = struct {
543+
pub const has_native_atomics = switch (cortex_m) {
544+
.cortex_m0, .cortex_m0plus => false,
545+
else => true,
546+
};
547+
548+
/// Atomic add
549+
pub fn add(comptime T: type, ptr: *T, delta: T) T {
550+
if (comptime has_native_atomics) {
551+
return @atomicRmw(T, ptr, .Add, delta, .monotonic);
552+
} else {
553+
interrupt.disable_interrupts();
554+
defer interrupt.enable_interrupts();
555+
const old_value = ptr.*;
556+
ptr.* = old_value +% delta;
557+
return old_value;
558+
}
559+
}
560+
561+
/// Atomic load
562+
pub fn load(comptime T: type, ptr: *const T, comptime ordering: std.builtin.AtomicOrder) T {
563+
if (comptime has_native_atomics) {
564+
return @atomicLoad(T, ptr, ordering);
565+
} else {
566+
interrupt.disable_interrupts();
567+
defer interrupt.enable_interrupts();
568+
return ptr.*;
569+
}
570+
}
571+
572+
/// Atomic store
573+
pub fn store(comptime T: type, ptr: *T, value: T, comptime ordering: std.builtin.AtomicOrder) void {
574+
if (comptime has_native_atomics) {
575+
@atomicStore(T, ptr, value, ordering);
576+
} else {
577+
interrupt.disable_interrupts();
578+
defer interrupt.enable_interrupts();
579+
ptr.* = value;
580+
}
581+
}
582+
583+
/// Atomic compare and swap
584+
pub fn cmpxchg(comptime T: type, ptr: *T, expected_value: T, new_value: T, comptime success_ordering: std.builtin.AtomicOrder, comptime failure_ordering: std.builtin.AtomicOrder) ?T {
585+
if (comptime has_native_atomics) {
586+
return @cmpxchgWeak(T, ptr, expected_value, new_value, success_ordering, failure_ordering);
587+
} else {
588+
interrupt.disable_interrupts();
589+
defer interrupt.enable_interrupts();
590+
const current = ptr.*;
591+
if (current == expected_value) {
592+
ptr.* = new_value;
593+
return null;
594+
}
595+
return current;
596+
}
597+
}
598+
599+
/// Atomic read-modify-write
600+
pub fn rmw(comptime T: type, ptr: *T, comptime op: std.builtin.AtomicRmwOp, operand: T, comptime ordering: std.builtin.AtomicOrder) T {
601+
if (comptime has_native_atomics) {
602+
return @atomicRmw(T, ptr, op, operand, ordering);
603+
} else {
604+
interrupt.disable_interrupts();
605+
defer interrupt.enable_interrupts();
606+
const old_value = ptr.*;
607+
ptr.* = switch (op) {
608+
.Xchg => operand,
609+
.Add => old_value +% operand,
610+
.Sub => old_value -% operand,
611+
.And => old_value & operand,
612+
.Nand => ~(old_value & operand),
613+
.Or => old_value | operand,
614+
.Xor => old_value ^ operand,
615+
.Max => @max(old_value, operand),
616+
.Min => @min(old_value, operand),
617+
};
618+
return old_value;
619+
}
620+
}
621+
};
622+
541623
/// The RAM vector table used. You can swap interrupt handlers at runtime here.
542624
/// Available when using a RAM vector table or a RAM image.
543625
pub var ram_vector_table: VectorTable align(256) = if (using_ram_vector_table or is_ram_image)
@@ -624,7 +706,7 @@ pub const startup_logic = struct {
624706
}
625707

626708
// Apply user-set interrupts
627-
// TODO: We might want to fail compilation if any interruptt is already set, since that
709+
// TODO: We might want to fail compilation if any interrupt is already set, since that
628710
// could e.g. disable timekeeping
629711
for (@typeInfo(@TypeOf(microzig.options.interrupts)).@"struct".fields) |field| {
630712
const maybe_handler = @field(microzig.options.interrupts, field.name);

port/nordic/nrf5x/src/hal/time.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const COMPARE_INDEX = 2;
2323
const TIMER_BITS = 23;
2424

2525
/// Stored the high bits of the current time, giving us 55 (23+32) instead of just 24 bits
26-
/// Must use @atomic to load an store from here.
26+
/// Must use atomic operations to load/store
2727
var period: u32 = 0;
2828

2929
pub fn init() void {
@@ -102,7 +102,7 @@ pub fn rtc_interrupt() callconv(.c) void {
102102
}
103103

104104
inline fn next_period() void {
105-
_ = @atomicRmw(u32, &period, .Add, 1, .monotonic);
105+
_ = microzig.cpu.atomic.add(u32, &period, 1);
106106
}
107107

108108
/// Calculate the full 55 bit value of the RTC. We have to take into account whether the period
@@ -112,7 +112,7 @@ fn calc_ticks(p: u32, counter: u24) u64 {
112112
}
113113

114114
pub fn get_time_since_boot() time.Absolute {
115-
const p = @atomicLoad(u32, &period, .acquire);
115+
const p = microzig.cpu.atomic.load(u32, &period, .acquire);
116116
const counter = rtc.COUNTER.read().COUNTER;
117117
const ticks = calc_ticks(p, counter);
118118
// RTC updates at 32768 hertz, so we can just multiply by 1M, then shift 15

0 commit comments

Comments
 (0)