Skip to content

Commit c7434ee

Browse files
authored
Rp2040 atomics (#545)
Co-authored-by: curuvar <[email protected]>
1 parent 031ce5c commit c7434ee

File tree

2 files changed

+178
-30
lines changed

2 files changed

+178
-30
lines changed

port/raspberrypi/rp2xxx/src/hal.zig

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const microzig = @import("microzig");
44
const SIO = microzig.chip.peripherals.SIO;
55

66
pub const adc = @import("hal/adc.zig");
7-
pub const atomic = @import("hal/atomic.zig");
87
pub const clocks = @import("hal/clocks.zig");
98
pub const dma = @import("hal/dma.zig");
109
pub const flash = @import("hal/flash.zig");
@@ -37,7 +36,15 @@ comptime {
3736
// HACK: tests can't access microzig. maybe there's a better way to do this.
3837
if (!builtin.is_test) {
3938
_ = image_def;
39+
}
40+
}
4041

42+
// On the RP2040, we need to import the `atomic.zig` file to export some global
43+
// functions that are used by the atomic builtins. Other chips have hardware
44+
// atomics, so we don't need to export those functions for them.
45+
46+
comptime {
47+
if (!builtin.is_test and compatibility.chip == .RP2040) {
4148
_ = @import("hal/atomic.zig");
4249
}
4350
}
Lines changed: 170 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
// Based on: https://github.com/ziglang/zig/blob/79460d4a3eef8eb927b02a7eda8bc9999a766672/lib/compiler_rt/atomics.zig
2+
// and: https://github.com/raspberrypi/pico-sdk/blob/ee68c78d0afae2b69c03ae1a72bf5cc267a2d94c/src/rp2_common/pico_atomic/atomic.c
3+
4+
//! Atomic operations for RP2040
5+
//!
6+
//! These functions should not be called directly. Instead, use the Zig atomic
7+
//! builtins or `std.atomic.Value`.
8+
19
const std = @import("std");
210
const builtin = @import("builtin");
311
const microzig = @import("microzig");
@@ -15,60 +23,193 @@ inline fn atomic_unlock(critical_section: CriticalSection) void {
1523
atomic_spinlock.unlock_irq(critical_section);
1624
}
1725

26+
fn atomic_store(comptime T: type, ptr: *volatile T, val: T, _: i32) void {
27+
const save = atomic_lock();
28+
defer atomic_unlock(save);
29+
ptr.* = val;
30+
}
31+
1832
fn atomic_load(comptime T: type, ptr: *volatile T, _: i32) T {
1933
const save = atomic_lock();
2034
defer atomic_unlock(save);
2135
const val = ptr.*;
2236
return val;
2337
}
2438

25-
fn atomic_rmw_and(comptime T: type, ptr: *volatile T, val: T, _: i32) T {
39+
fn atomic_rmw(comptime T: type, ptr: *volatile T, val: T, _: i32, comptime op: std.builtin.AtomicRmwOp) T {
2640
const save = atomic_lock();
2741
defer atomic_unlock(save);
2842
const tmp = ptr.*;
29-
ptr.* = tmp & val;
43+
44+
switch (op) {
45+
.Xchg => ptr.* = val,
46+
.Add => ptr.* += val,
47+
.Sub => ptr.* -= val,
48+
.And => ptr.* &= val,
49+
.Or => ptr.* |= val,
50+
.Xor => ptr.* ^= val,
51+
.Nand => ptr.* = ~(ptr.* & val),
52+
.Max => ptr.* = @max(ptr.*, val),
53+
.Min => ptr.* = @min(ptr.*, val),
54+
}
55+
3056
return tmp;
3157
}
3258

33-
fn atomic_rmw_or(comptime T: type, ptr: *volatile T, val: T, _: i32) T {
59+
fn atomic_compare_exchange(comptime T: type, ptr: *volatile T, expected: *T, desired: T, _: i32, _: i32) bool {
3460
const save = atomic_lock();
3561
defer atomic_unlock(save);
36-
const tmp = ptr.*;
37-
ptr.* = tmp | val;
38-
return tmp;
62+
const old_value = ptr.*;
63+
64+
if (old_value == expected.*) {
65+
ptr.* = desired;
66+
return true;
67+
} else {
68+
expected.* = old_value;
69+
return false;
70+
}
3971
}
4072

41-
fn __atomic_fetch_and_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
42-
return atomic_rmw_and(u32, ptr, val, model);
73+
export fn __atomic_store_1(ptr: *u8, val: u8, model: i32) callconv(.c) void {
74+
atomic_store(u8, ptr, val, model);
4375
}
4476

45-
fn __atomic_fetch_or_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
46-
return atomic_rmw_or(u32, ptr, val, model);
77+
export fn __atomic_store_2(ptr: *u16, val: u16, model: i32) callconv(.c) void {
78+
atomic_store(u16, ptr, val, model);
4779
}
4880

49-
fn __atomic_load_4(src: *u32, model: i32) callconv(.c) u32 {
50-
return atomic_load(u32, src, model);
81+
export fn __atomic_store_4(ptr: *u32, val: u32, model: i32) callconv(.c) void {
82+
atomic_store(u32, ptr, val, model);
5183
}
5284

53-
const linkage: std.builtin.GlobalLinkage = if (builtin.is_test)
54-
.internal
55-
else if (builtin.object_format == .c)
56-
.strong
57-
else
58-
.weak;
85+
export fn __atomic_load_1(ptr: *u8, model: i32) callconv(.c) u8 {
86+
return atomic_load(u8, ptr, model);
87+
}
5988

60-
const visibility: std.builtin.SymbolVisibility = .default;
89+
export fn __atomic_load_2(ptr: *u16, model: i32) callconv(.c) u16 {
90+
return atomic_load(u16, ptr, model);
91+
}
6192

62-
// Based on: https://github.com/ziglang/zig/blob/79460d4a3eef8eb927b02a7eda8bc9999a766672/lib/compiler_rt/atomics.zig
63-
// and: https://github.com/raspberrypi/pico-sdk/blob/ee68c78d0afae2b69c03ae1a72bf5cc267a2d94c/src/rp2_common/pico_atomic/atomic.c
64-
// TODO: export all the rest atomics
93+
export fn __atomic_load_4(ptr: *u32, model: i32) callconv(.c) u32 {
94+
return atomic_load(u32, ptr, model);
95+
}
6596

66-
comptime {
67-
// This export should only happen for the RP2040 due to the ARMv6-M ISA used by the ARM Cortex-M0+.
68-
if (chip == .RP2040) {
69-
@export(&__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility });
70-
@export(&__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility });
97+
export fn __atomic_exchange_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
98+
return atomic_rmw(u8, ptr, val, model, .Xchg);
99+
}
71100

72-
@export(&__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility });
73-
}
101+
export fn __atomic_exchange_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
102+
return atomic_rmw(u16, ptr, val, model, .Xchg);
103+
}
104+
105+
export fn __atomic_exchange_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
106+
return atomic_rmw(u32, ptr, val, model, .Xchg);
107+
}
108+
109+
export fn __atomic_fetch_add_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
110+
return atomic_rmw(u8, ptr, val, model, .Add);
111+
}
112+
113+
export fn __atomic_fetch_add_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
114+
return atomic_rmw(u16, ptr, val, model, .Add);
115+
}
116+
117+
export fn __atomic_fetch_add_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
118+
return atomic_rmw(u32, ptr, val, model, .Add);
119+
}
120+
121+
export fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
122+
return atomic_rmw(u8, ptr, val, model, .Sub);
123+
}
124+
125+
export fn __atomic_fetch_sub_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
126+
return atomic_rmw(u16, ptr, val, model, .Sub);
127+
}
128+
129+
export fn __atomic_fetch_sub_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
130+
return atomic_rmw(u32, ptr, val, model, .Sub);
131+
}
132+
133+
export fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
134+
return atomic_rmw(u8, ptr, val, model, .And);
135+
}
136+
137+
export fn __atomic_fetch_and_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
138+
return atomic_rmw(u16, ptr, val, model, .And);
139+
}
140+
141+
export fn __atomic_fetch_and_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
142+
return atomic_rmw(u32, ptr, val, model, .And);
143+
}
144+
145+
export fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
146+
return atomic_rmw(u8, ptr, val, model, .Or);
147+
}
148+
149+
export fn __atomic_fetch_or_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
150+
return atomic_rmw(u16, ptr, val, model, .Or);
151+
}
152+
153+
export fn __atomic_fetch_or_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
154+
return atomic_rmw(u32, ptr, val, model, .Or);
155+
}
156+
157+
export fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
158+
return atomic_rmw(u8, ptr, val, model, .Xor);
159+
}
160+
161+
export fn __atomic_fetch_xor_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
162+
return atomic_rmw(u16, ptr, val, model, .Xor);
163+
}
164+
165+
export fn __atomic_fetch_xor_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
166+
return atomic_rmw(u32, ptr, val, model, .Xor);
167+
}
168+
169+
export fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
170+
return atomic_rmw(u8, ptr, val, model, .Nand);
171+
}
172+
173+
export fn __atomic_fetch_nand_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
174+
return atomic_rmw(u16, ptr, val, model, .Nand);
175+
}
176+
177+
export fn __atomic_fetch_nand_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
178+
return atomic_rmw(u32, ptr, val, model, .Nand);
179+
}
180+
181+
export fn __atomic_fetch_max_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
182+
return atomic_rmw(u8, ptr, val, model, .Max);
183+
}
184+
185+
export fn __atomic_fetch_max_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
186+
return atomic_rmw(u16, ptr, val, model, .Max);
187+
}
188+
189+
export fn __atomic_fetch_max_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
190+
return atomic_rmw(u32, ptr, val, model, .Max);
191+
}
192+
193+
export fn __atomic_fetch_min_1(ptr: *u8, val: u8, model: i32) callconv(.c) u8 {
194+
return atomic_rmw(u8, ptr, val, model, .Min);
195+
}
196+
197+
export fn __atomic_fetch_min_2(ptr: *u16, val: u16, model: i32) callconv(.c) u16 {
198+
return atomic_rmw(u16, ptr, val, model, .Min);
199+
}
200+
201+
export fn __atomic_fetch_min_4(ptr: *u32, val: u32, model: i32) callconv(.c) u32 {
202+
return atomic_rmw(u32, ptr, val, model, .Min);
203+
}
204+
205+
export fn __atomic_compare_exchange_1(ptr: *u8, expected: *u8, desired: u8, success: i32, failure: i32) callconv(.c) bool {
206+
return atomic_compare_exchange(u8, ptr, expected, desired, success, failure);
207+
}
208+
209+
export fn __atomic_compare_exchange_2(ptr: *u16, expected: *u16, desired: u16, success: i32, failure: i32) callconv(.c) bool {
210+
return atomic_compare_exchange(u16, ptr, expected, desired, success, failure);
211+
}
212+
213+
export fn __atomic_compare_exchange_4(ptr: *u32, expected: *u32, desired: u32, success: i32, failure: i32) callconv(.c) bool {
214+
return atomic_compare_exchange(u32, ptr, expected, desired, success, failure);
74215
}

0 commit comments

Comments
 (0)