switchboard-hw 0.3.0__cp314-cp314-macosx_11_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. _switchboard.cpython-314-darwin.so +0 -0
  2. switchboard/__init__.py +24 -0
  3. switchboard/ams.py +668 -0
  4. switchboard/apb.py +278 -0
  5. switchboard/autowrap.py +1000 -0
  6. switchboard/axi.py +571 -0
  7. switchboard/axil.py +348 -0
  8. switchboard/bitvector.py +112 -0
  9. switchboard/cmdline.py +142 -0
  10. switchboard/cpp/Makefile +13 -0
  11. switchboard/cpp/bitutil.h +39 -0
  12. switchboard/cpp/pagemap.h +91 -0
  13. switchboard/cpp/pciedev.h +86 -0
  14. switchboard/cpp/router.cc +89 -0
  15. switchboard/cpp/spsc_queue.h +267 -0
  16. switchboard/cpp/switchboard.hpp +257 -0
  17. switchboard/cpp/switchboard_pcie.hpp +234 -0
  18. switchboard/cpp/switchboard_tlm.hpp +98 -0
  19. switchboard/cpp/umilib.h +144 -0
  20. switchboard/cpp/umilib.hpp +113 -0
  21. switchboard/cpp/umisb.hpp +364 -0
  22. switchboard/cpp/xyce.hpp +90 -0
  23. switchboard/deps/__init__.py +0 -0
  24. switchboard/deps/verilog_axi.py +23 -0
  25. switchboard/dpi/__init__.py +0 -0
  26. switchboard/dpi/switchboard_dpi.cc +119 -0
  27. switchboard/dpi/switchboard_dpi.py +13 -0
  28. switchboard/dpi/xyce_dpi.cc +43 -0
  29. switchboard/gpio.py +108 -0
  30. switchboard/icarus.py +85 -0
  31. switchboard/loopback.py +157 -0
  32. switchboard/network.py +714 -0
  33. switchboard/pytest_plugin.py +11 -0
  34. switchboard/sbdesign.py +55 -0
  35. switchboard/sbdut.py +744 -0
  36. switchboard/sbtcp.py +345 -0
  37. switchboard/sc/__init__.py +0 -0
  38. switchboard/sc/morty/__init__.py +0 -0
  39. switchboard/sc/morty/uniquify.py +67 -0
  40. switchboard/sc/sed/__init__.py +0 -0
  41. switchboard/sc/sed/sed_remove.py +47 -0
  42. switchboard/sc/standalone_netlist_flow.py +25 -0
  43. switchboard/switchboard.py +53 -0
  44. switchboard/test_util.py +46 -0
  45. switchboard/uart_xactor.py +66 -0
  46. switchboard/umi.py +793 -0
  47. switchboard/util.py +131 -0
  48. switchboard/verilator/__init__.py +0 -0
  49. switchboard/verilator/config.vlt +13 -0
  50. switchboard/verilator/testbench.cc +143 -0
  51. switchboard/verilator/verilator.py +13 -0
  52. switchboard/verilator_run.py +31 -0
  53. switchboard/verilog/__init__.py +0 -0
  54. switchboard/verilog/common/__init__.py +0 -0
  55. switchboard/verilog/common/common.py +26 -0
  56. switchboard/verilog/common/switchboard.vh +429 -0
  57. switchboard/verilog/common/uart_xactor.sv +247 -0
  58. switchboard/verilog/common/umi_gpio.v +236 -0
  59. switchboard/verilog/fpga/__init__.py +0 -0
  60. switchboard/verilog/fpga/axi_reader.sv +82 -0
  61. switchboard/verilog/fpga/axi_writer.sv +111 -0
  62. switchboard/verilog/fpga/config_registers.sv +249 -0
  63. switchboard/verilog/fpga/fpga.py +21 -0
  64. switchboard/verilog/fpga/include/sb_queue_regmap.vh +21 -0
  65. switchboard/verilog/fpga/include/spsc_queue.vh +7 -0
  66. switchboard/verilog/fpga/memory_fault.sv +40 -0
  67. switchboard/verilog/fpga/sb_fpga_queues.sv +416 -0
  68. switchboard/verilog/fpga/sb_rx_fpga.sv +303 -0
  69. switchboard/verilog/fpga/sb_tx_fpga.sv +294 -0
  70. switchboard/verilog/fpga/umi_fpga_queues.sv +146 -0
  71. switchboard/verilog/sim/__init__.py +0 -0
  72. switchboard/verilog/sim/auto_stop_sim.sv +25 -0
  73. switchboard/verilog/sim/perf_meas_sim.sv +97 -0
  74. switchboard/verilog/sim/queue_to_sb_sim.sv +176 -0
  75. switchboard/verilog/sim/queue_to_umi_sim.sv +66 -0
  76. switchboard/verilog/sim/sb_apb_m.sv +146 -0
  77. switchboard/verilog/sim/sb_axi_m.sv +199 -0
  78. switchboard/verilog/sim/sb_axil_m.sv +180 -0
  79. switchboard/verilog/sim/sb_axil_s.sv +180 -0
  80. switchboard/verilog/sim/sb_clk_gen.sv +89 -0
  81. switchboard/verilog/sim/sb_jtag_rbb_sim.sv +148 -0
  82. switchboard/verilog/sim/sb_rx_sim.sv +55 -0
  83. switchboard/verilog/sim/sb_to_queue_sim.sv +196 -0
  84. switchboard/verilog/sim/sb_tx_sim.sv +55 -0
  85. switchboard/verilog/sim/switchboard_sim.py +49 -0
  86. switchboard/verilog/sim/umi_rx_sim.sv +61 -0
  87. switchboard/verilog/sim/umi_to_queue_sim.sv +66 -0
  88. switchboard/verilog/sim/umi_tx_sim.sv +61 -0
  89. switchboard/verilog/sim/xyce_intf.sv +67 -0
  90. switchboard/vpi/switchboard_vpi.cc +431 -0
  91. switchboard/vpi/xyce_vpi.cc +200 -0
  92. switchboard/warn.py +14 -0
  93. switchboard/xyce.py +27 -0
  94. switchboard_hw-0.3.0.dist-info/METADATA +303 -0
  95. switchboard_hw-0.3.0.dist-info/RECORD +99 -0
  96. switchboard_hw-0.3.0.dist-info/WHEEL +6 -0
  97. switchboard_hw-0.3.0.dist-info/entry_points.txt +6 -0
  98. switchboard_hw-0.3.0.dist-info/licenses/LICENSE +190 -0
  99. switchboard_hw-0.3.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,91 @@
1
+ // Linux user-space virt-to-phys mapper
2
+
3
+ // Copyright (c) 2024 Zero ASIC Corporation
4
+ // This code is licensed under Apache License 2.0 (see LICENSE for details)
5
+
6
+ #ifndef PAGEMAP_H_
7
+ #define PAGEMAP_H_
8
+
9
+ #include <assert.h>
10
+ #include <inttypes.h>
11
+ #include <stdint.h>
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+
15
+ #include <fcntl.h>
16
+ #include <sys/stat.h>
17
+ #include <sys/types.h>
18
+ #include <unistd.h>
19
+
20
+ /*
21
+ * From https://www.kernel.org/doc/Documentation/admin-guide/mm/pagemap.rst
22
+
23
+ * ``/proc/pid/pagemap``. This file lets a userspace process find out which
24
+ physical frame each virtual page is mapped to. It contains one 64-bit
25
+ value for each virtual page, containing the following data (from
26
+ ``fs/proc/task_mmu.c``, above pagemap_read):
27
+
28
+ * Bits 0-54 page frame number (PFN) if present
29
+ * Bits 0-4 swap type if swapped
30
+ * Bits 5-54 swap offset if swapped
31
+ * Bit 55 pte is soft-dirty (see
32
+ :ref:`Documentation/admin-guide/mm/soft-dirty.rst <soft_dirty>`)
33
+ * Bit 56 page exclusively mapped (since 4.2)
34
+ * Bit 57 pte is uffd-wp write-protected (since 5.13) (see
35
+ :ref:`Documentation/admin-guide/mm/userfaultfd.rst <userfaultfd>`)
36
+ * Bits 58-60 zero
37
+ * Bit 61 page is file-page or shared-anon (since 3.5)
38
+ * Bit 62 page swapped
39
+ * Bit 63 page present
40
+ */
41
+
42
+ #define PAGEMAP_PFN_MASK ((1ULL << 55) - 1)
43
+ #define PAGEMAP_PAGE_PRESENT (1ULL << 63)
44
+ #define PAGEMAP_FAILED (~0ULL)
45
+
46
+ static inline int pagemap_open_self(void) {
47
+ int r;
48
+
49
+ r = open("/proc/self/pagemap", O_RDONLY);
50
+ if (r < 0) {
51
+ perror("open");
52
+ }
53
+ return r;
54
+ }
55
+
56
+ // Translate a given virtual ptr into its physical address.
57
+ static inline uint64_t pagemap_virt_to_phys(void* ptr) {
58
+ uint64_t va = (uintptr_t)ptr;
59
+ uint64_t pagemap;
60
+ uint64_t offset;
61
+ uint64_t vfn;
62
+ uint64_t pa;
63
+ int pagesize;
64
+ ssize_t r;
65
+ int fd;
66
+
67
+ fd = pagemap_open_self();
68
+ if (fd < 0) {
69
+ return PAGEMAP_FAILED;
70
+ }
71
+
72
+ pagesize = getpagesize();
73
+ offset = va % pagesize;
74
+ vfn = va / pagesize;
75
+ r = pread(fd, &pagemap, sizeof pagemap, 8 * vfn);
76
+ assert(r == sizeof pagemap);
77
+ close(fd);
78
+
79
+ if (!(pagemap & PAGEMAP_PAGE_PRESENT)) {
80
+ return PAGEMAP_FAILED;
81
+ }
82
+
83
+ pa = (pagemap & PAGEMAP_PFN_MASK) * pagesize;
84
+ if (!pa) {
85
+ return PAGEMAP_FAILED;
86
+ }
87
+
88
+ pa |= offset;
89
+ return pa;
90
+ }
91
+ #endif
@@ -0,0 +1,86 @@
1
+ // Copyright (c) 2024 Zero ASIC Corporation
2
+ // This code is licensed under Apache License 2.0 (see LICENSE for details)
3
+
4
+ #ifndef PCIEDEV_H__
5
+ #define PCIEDEV_H__
6
+
7
+ #include <assert.h>
8
+ #include <fcntl.h>
9
+ #include <stdint.h>
10
+ #include <stdio.h>
11
+ #include <sys/stat.h>
12
+ #include <sys/types.h>
13
+
14
+ #include <sys/mman.h>
15
+
16
+ #define PCIE_READ_GEN(t, suffix) \
17
+ static inline t pcie_read##suffix(void* p) { \
18
+ uintptr_t i = (uintptr_t)p; \
19
+ t val; \
20
+ \
21
+ /* Enforce alignment. */ \
22
+ assert((i % sizeof(val)) == 0); \
23
+ \
24
+ /* Make access. */ \
25
+ return *(volatile t*)p; \
26
+ }
27
+
28
+ #define PCIE_WRITE_GEN(t, suffix) \
29
+ static inline void pcie_write##suffix(void* p, t v) { \
30
+ uintptr_t i = (uintptr_t)p; \
31
+ t val; \
32
+ \
33
+ /* Enforce alignment. */ \
34
+ assert((i % sizeof(val)) == 0); \
35
+ \
36
+ /* Make access. */ \
37
+ *(volatile t*)p = v; \
38
+ } \
39
+ \
40
+ static inline void pcie_write##suffix##_strong(void* p, t v) { \
41
+ t dummy; \
42
+ pcie_write##suffix(p, v); \
43
+ /* Enforce PCI ordering by reading back the same reg. */ \
44
+ dummy = pcie_read##suffix(p); \
45
+ dummy = dummy; \
46
+ }
47
+
48
+ PCIE_READ_GEN(uint64_t, 64)
49
+ PCIE_WRITE_GEN(uint64_t, 64)
50
+ PCIE_READ_GEN(uint32_t, 32)
51
+ PCIE_WRITE_GEN(uint32_t, 32)
52
+ PCIE_READ_GEN(uint16_t, 16)
53
+ PCIE_WRITE_GEN(uint16_t, 16)
54
+ PCIE_READ_GEN(uint8_t, 8)
55
+ PCIE_WRITE_GEN(uint8_t, 8)
56
+
57
+ static inline void* pcie_bar_map(const char* bdf, int bar_num, uint64_t offset, uint64_t size) {
58
+ char name[] = "/sys/bus/pci/devices/XXXX:XX:XX.X/resourceYY";
59
+ void* p = MAP_FAILED;
60
+ int fd = -1;
61
+
62
+ snprintf(name, sizeof name, "/sys/bus/pci/devices/%s/resource%d", bdf, bar_num);
63
+
64
+ fd = open(name, O_RDWR | O_SYNC);
65
+ if (fd < 0) {
66
+ goto done;
67
+ }
68
+
69
+ p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
70
+
71
+ done:
72
+ if (fd > 0) {
73
+ close(fd);
74
+ }
75
+ return p;
76
+ }
77
+
78
+ static inline void pcie_bar_unmap(void* p, uint64_t size) {
79
+ int r;
80
+
81
+ r = munmap(p, size);
82
+ if (r < 0) {
83
+ perror("munmap");
84
+ }
85
+ }
86
+ #endif
@@ -0,0 +1,89 @@
1
+ // Copyright (c) 2024 Zero ASIC Corporation
2
+ // This code is licensed under Apache License 2.0 (see LICENSE for details)
3
+
4
+ #include <cstdlib>
5
+ #include <iostream>
6
+ #include <map>
7
+ #include <memory>
8
+ #include <thread>
9
+ #include <vector>
10
+
11
+ #include "switchboard.hpp"
12
+
13
+ // connections to each of the entries in the grid
14
+ std::map<int, int> routing_table;
15
+ std::map<int, std::unique_ptr<SBTX>> txconn;
16
+ std::vector<std::unique_ptr<SBRX>> rxconn;
17
+
18
+ bool init(int argc, char* argv[]) {
19
+ // determine number of rows and columns
20
+
21
+ int arg_idx = 1;
22
+
23
+ enum MODE { RX, TX, ROUTE, UNDEF };
24
+ MODE mode = UNDEF;
25
+
26
+ while (arg_idx < argc) {
27
+ std::string arg = std::string(argv[arg_idx++]);
28
+ if (arg == "--rx") {
29
+ mode = RX;
30
+ } else if (arg == "--tx") {
31
+ mode = TX;
32
+ } else if (arg == "--route") {
33
+ mode = ROUTE;
34
+ } else if (mode == RX) {
35
+ rxconn.push_back(std::unique_ptr<SBRX>(new SBRX()));
36
+ rxconn.back()->init(std::string("queue-") + arg);
37
+ } else if (mode == TX) {
38
+ int queue = atoi(arg.c_str());
39
+ txconn[queue] = std::unique_ptr<SBTX>(new SBTX());
40
+ txconn[queue]->init(std::string("queue-") + arg);
41
+ } else if (mode == ROUTE) {
42
+ size_t split = arg.find(':');
43
+ std::string first = arg.substr(0, split);
44
+ std::string second = arg.substr(split + 1);
45
+ int dest = atoi(first.c_str());
46
+ int queue = atoi(second.c_str());
47
+ routing_table[dest] = queue;
48
+ } else {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ return true;
54
+ }
55
+
56
+ int main(int argc, char* argv[]) {
57
+ // set up connections
58
+ if (!init(argc, argv)) {
59
+ printf("ERROR: arguments are not formed properly.\n");
60
+ return 1;
61
+ }
62
+
63
+ sb_packet p;
64
+ while (true) {
65
+ // loop over all RX connection
66
+ for (auto& rx : rxconn) {
67
+ if (rx->is_active()) {
68
+ if (rx->recv_peek(p)) {
69
+ // make sure that the destination is in the routing table and active
70
+ if ((routing_table.count(p.destination) > 0) &&
71
+ (txconn.count(routing_table[p.destination]) > 0) &&
72
+ (txconn[routing_table[p.destination]]->is_active())) {
73
+
74
+ // try to send the packet, removing it from
75
+ // the RX queue if the send is successful
76
+ if (txconn[routing_table[p.destination]]->send(p)) {
77
+ rx->recv();
78
+ }
79
+ } else {
80
+ printf("ERROR: Cannot route packet.\n");
81
+ return 1;
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ return 0;
89
+ }
@@ -0,0 +1,267 @@
1
+ // Single Producer Single Consumer Queue implemented over shared-memory
2
+
3
+ // Copyright (c) 2024 Zero ASIC Corporation
4
+ // This code is licensed under Apache License 2.0 (see LICENSE for details)
5
+
6
+ #ifndef SPSC_QUEUE_H__
7
+ #define SPSC_QUEUE_H__
8
+
9
+ #include <assert.h>
10
+ #include <fcntl.h>
11
+ #include <stdbool.h>
12
+ #include <stdint.h>
13
+ #include <stdio.h>
14
+ #include <stdlib.h>
15
+ #include <string.h>
16
+ #include <sys/mman.h>
17
+ #include <unistd.h>
18
+
19
+ #ifdef __cplusplus
20
+ #include <atomic>
21
+ using namespace std;
22
+ #else
23
+ #include <stdatomic.h>
24
+ #endif
25
+
26
+ #ifndef MAP_POPULATE
27
+ // If the implementation lacks MAP_POPULATE, define it to 0 (no-op).
28
+ #define MAP_POPULATE 0
29
+ #endif
30
+
31
+ #define SPSC_QUEUE_MAX_PACKET_SIZE 64
32
+ #define SPSC_QUEUE_CACHE_LINE_SIZE 64
33
+
34
+ typedef struct spsc_queue_shared {
35
+ int32_t head __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
36
+ int32_t tail __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
37
+ uint32_t packets[1][SPSC_QUEUE_MAX_PACKET_SIZE / 4]
38
+ __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
39
+ } spsc_queue_shared;
40
+
41
+ typedef struct spsc_queue {
42
+ int32_t cached_tail __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
43
+ int32_t cached_head __attribute__((__aligned__(SPSC_QUEUE_CACHE_LINE_SIZE)));
44
+ spsc_queue_shared* shm;
45
+ char* name;
46
+ int capacity;
47
+
48
+ bool unmap_at_close;
49
+ } spsc_queue;
50
+
51
+ // Returns the capacity of a queue given a specific mapsize.
52
+ static inline int spsc_capacity(size_t mapsize) {
53
+ spsc_queue* q = NULL;
54
+ int capacity;
55
+
56
+ if (mapsize < sizeof(*q->shm)) {
57
+ return 0;
58
+ }
59
+
60
+ // Start with the size of the shared area. This includes the
61
+ // control members + one packet.
62
+ mapsize -= sizeof(*q->shm);
63
+
64
+ capacity = mapsize / sizeof(q->shm->packets[0]) + 1;
65
+
66
+ if (capacity < 2) {
67
+ // Capacities less than 2 are invalid.
68
+ return 0;
69
+ }
70
+
71
+ return capacity;
72
+ }
73
+
74
+ static inline size_t spsc_mapsize(int capacity) {
75
+ spsc_queue* q = NULL;
76
+ size_t mapsize;
77
+
78
+ assert(capacity >= 2);
79
+
80
+ // Start with the size of the shared area. This includes the
81
+ // control members + one packet.
82
+ mapsize = sizeof(*q->shm);
83
+ // Add additional packets.
84
+ mapsize += sizeof(q->shm->packets[0]) * (capacity - 1);
85
+
86
+ return mapsize;
87
+ }
88
+
89
+ static inline spsc_queue* spsc_open_mem(const char* name, size_t capacity, void* mem) {
90
+ spsc_queue* q = NULL;
91
+ size_t mapsize;
92
+ void* p;
93
+ int fd = -1;
94
+ int r;
95
+
96
+ // Compute the size of the SHM mapping.
97
+ mapsize = spsc_mapsize(capacity);
98
+
99
+ // Allocate a cache-line aligned spsc-queue.
100
+ r = posix_memalign(&p, SPSC_QUEUE_CACHE_LINE_SIZE, sizeof(spsc_queue));
101
+ if (r) {
102
+ fprintf(stderr, "posix_memalign: %s\n", strerror(r));
103
+ goto err;
104
+ }
105
+ q = (spsc_queue*)p;
106
+ memset(q, 0, sizeof *q);
107
+
108
+ p = mem;
109
+ if (!mem) {
110
+ fd = open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
111
+ if (fd < 0) {
112
+ perror(name);
113
+ goto err;
114
+ }
115
+
116
+ r = ftruncate(fd, mapsize);
117
+ if (r < 0) {
118
+ perror("ftruncate");
119
+ goto err;
120
+ }
121
+
122
+ // Map a shared file-backed mapping for the SHM area.
123
+ // This will always be page-aligned.
124
+ p = mmap(NULL, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, fd, 0);
125
+
126
+ if (p == MAP_FAILED) {
127
+ perror("mmap");
128
+ goto err;
129
+ }
130
+
131
+ // We can now close the fd without affecting active mmaps.
132
+ close(fd);
133
+ q->unmap_at_close = true;
134
+ }
135
+
136
+ q->shm = (spsc_queue_shared*)p;
137
+ q->name = strdup(name);
138
+ q->capacity = capacity;
139
+
140
+ /* In case we're opening a pre-existing queue, pick up where we left off. */
141
+ __atomic_load(&q->shm->tail, &q->cached_tail, __ATOMIC_RELAXED);
142
+ __atomic_load(&q->shm->head, &q->cached_head, __ATOMIC_RELAXED);
143
+ return q;
144
+
145
+ err:
146
+ if (fd > 0) {
147
+ close(fd);
148
+ }
149
+ free(q);
150
+ return NULL;
151
+ }
152
+
153
+ static inline int spsc_mlock(spsc_queue* q) {
154
+ size_t mapsize = spsc_mapsize(q->capacity);
155
+
156
+ return mlock(q->shm, mapsize);
157
+ }
158
+
159
+ static inline spsc_queue* spsc_open(const char* name, size_t capacity) {
160
+ return spsc_open_mem(name, capacity, NULL);
161
+ }
162
+
163
+ static inline void spsc_remove_shmfile(const char* name) {
164
+ remove(name);
165
+ }
166
+
167
+ static inline void spsc_close(spsc_queue* q) {
168
+ size_t mapsize;
169
+
170
+ if (!q) {
171
+ return;
172
+ }
173
+
174
+ mapsize = spsc_mapsize(q->capacity);
175
+
176
+ // We've already closed the file-descriptor. We now need to munmap the mmap.
177
+ if (q->unmap_at_close) {
178
+ munmap(q->shm, mapsize);
179
+ }
180
+
181
+ free(q->name);
182
+ free(q);
183
+ }
184
+
185
+ static inline int spsc_size(spsc_queue* q) {
186
+ int head, tail;
187
+ int size;
188
+
189
+ __atomic_load(&q->shm->head, &head, __ATOMIC_ACQUIRE);
190
+ __atomic_load(&q->shm->tail, &tail, __ATOMIC_ACQUIRE);
191
+
192
+ size = head - tail;
193
+ if (size < 0) {
194
+ size += q->capacity;
195
+ }
196
+ return size;
197
+ }
198
+
199
+ static inline bool spsc_send(spsc_queue* q, void* buf, size_t size) {
200
+ // get pointer to head
201
+ int head;
202
+
203
+ __atomic_load(&q->shm->head, &head, __ATOMIC_RELAXED);
204
+
205
+ assert(size <= sizeof q->shm->packets[0]);
206
+
207
+ // compute the head pointer
208
+ int next_head = head + 1;
209
+ if (next_head == q->capacity) {
210
+ next_head = 0;
211
+ }
212
+
213
+ // if the queue is full, bail out
214
+ if (next_head == q->cached_tail) {
215
+ __atomic_load(&q->shm->tail, &q->cached_tail, __ATOMIC_ACQUIRE);
216
+ if (next_head == q->cached_tail) {
217
+ return false;
218
+ }
219
+ }
220
+
221
+ // otherwise write in the packet
222
+ memcpy(q->shm->packets[head], buf, size);
223
+
224
+ // and update the head pointer
225
+ __atomic_store(&q->shm->head, &next_head, __ATOMIC_RELEASE);
226
+
227
+ return true;
228
+ }
229
+
230
+ static inline bool spsc_recv_base(spsc_queue* q, void* buf, size_t size, bool pop) {
231
+ // get the read pointer
232
+ int tail;
233
+ __atomic_load(&q->shm->tail, &tail, __ATOMIC_RELAXED);
234
+
235
+ assert(size <= sizeof q->shm->packets[0]);
236
+
237
+ // if the queue is empty, bail out
238
+ if (tail == q->cached_head) {
239
+ __atomic_load(&q->shm->head, &q->cached_head, __ATOMIC_ACQUIRE);
240
+ if (tail == q->cached_head) {
241
+ return false;
242
+ }
243
+ }
244
+
245
+ // otherwise read out the packet
246
+ memcpy(buf, q->shm->packets[tail], size);
247
+
248
+ if (pop) {
249
+ // and update the read pointer
250
+ tail++;
251
+ if (tail == q->capacity) {
252
+ tail = 0;
253
+ }
254
+ __atomic_store(&q->shm->tail, &tail, __ATOMIC_RELEASE);
255
+ }
256
+
257
+ return true;
258
+ }
259
+
260
+ static inline bool spsc_recv(spsc_queue* q, void* buf, size_t size) {
261
+ return spsc_recv_base(q, buf, size, true);
262
+ }
263
+
264
+ static inline bool spsc_recv_peek(spsc_queue* q, void* buf, size_t size) {
265
+ return spsc_recv_base(q, buf, size, false);
266
+ }
267
+ #endif // _SPSC_QUEUE