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.
- _switchboard.cpython-314-darwin.so +0 -0
- switchboard/__init__.py +24 -0
- switchboard/ams.py +668 -0
- switchboard/apb.py +278 -0
- switchboard/autowrap.py +1000 -0
- switchboard/axi.py +571 -0
- switchboard/axil.py +348 -0
- switchboard/bitvector.py +112 -0
- switchboard/cmdline.py +142 -0
- switchboard/cpp/Makefile +13 -0
- switchboard/cpp/bitutil.h +39 -0
- switchboard/cpp/pagemap.h +91 -0
- switchboard/cpp/pciedev.h +86 -0
- switchboard/cpp/router.cc +89 -0
- switchboard/cpp/spsc_queue.h +267 -0
- switchboard/cpp/switchboard.hpp +257 -0
- switchboard/cpp/switchboard_pcie.hpp +234 -0
- switchboard/cpp/switchboard_tlm.hpp +98 -0
- switchboard/cpp/umilib.h +144 -0
- switchboard/cpp/umilib.hpp +113 -0
- switchboard/cpp/umisb.hpp +364 -0
- switchboard/cpp/xyce.hpp +90 -0
- switchboard/deps/__init__.py +0 -0
- switchboard/deps/verilog_axi.py +23 -0
- switchboard/dpi/__init__.py +0 -0
- switchboard/dpi/switchboard_dpi.cc +119 -0
- switchboard/dpi/switchboard_dpi.py +13 -0
- switchboard/dpi/xyce_dpi.cc +43 -0
- switchboard/gpio.py +108 -0
- switchboard/icarus.py +85 -0
- switchboard/loopback.py +157 -0
- switchboard/network.py +714 -0
- switchboard/pytest_plugin.py +11 -0
- switchboard/sbdesign.py +55 -0
- switchboard/sbdut.py +744 -0
- switchboard/sbtcp.py +345 -0
- switchboard/sc/__init__.py +0 -0
- switchboard/sc/morty/__init__.py +0 -0
- switchboard/sc/morty/uniquify.py +67 -0
- switchboard/sc/sed/__init__.py +0 -0
- switchboard/sc/sed/sed_remove.py +47 -0
- switchboard/sc/standalone_netlist_flow.py +25 -0
- switchboard/switchboard.py +53 -0
- switchboard/test_util.py +46 -0
- switchboard/uart_xactor.py +66 -0
- switchboard/umi.py +793 -0
- switchboard/util.py +131 -0
- switchboard/verilator/__init__.py +0 -0
- switchboard/verilator/config.vlt +13 -0
- switchboard/verilator/testbench.cc +143 -0
- switchboard/verilator/verilator.py +13 -0
- switchboard/verilator_run.py +31 -0
- switchboard/verilog/__init__.py +0 -0
- switchboard/verilog/common/__init__.py +0 -0
- switchboard/verilog/common/common.py +26 -0
- switchboard/verilog/common/switchboard.vh +429 -0
- switchboard/verilog/common/uart_xactor.sv +247 -0
- switchboard/verilog/common/umi_gpio.v +236 -0
- switchboard/verilog/fpga/__init__.py +0 -0
- switchboard/verilog/fpga/axi_reader.sv +82 -0
- switchboard/verilog/fpga/axi_writer.sv +111 -0
- switchboard/verilog/fpga/config_registers.sv +249 -0
- switchboard/verilog/fpga/fpga.py +21 -0
- switchboard/verilog/fpga/include/sb_queue_regmap.vh +21 -0
- switchboard/verilog/fpga/include/spsc_queue.vh +7 -0
- switchboard/verilog/fpga/memory_fault.sv +40 -0
- switchboard/verilog/fpga/sb_fpga_queues.sv +416 -0
- switchboard/verilog/fpga/sb_rx_fpga.sv +303 -0
- switchboard/verilog/fpga/sb_tx_fpga.sv +294 -0
- switchboard/verilog/fpga/umi_fpga_queues.sv +146 -0
- switchboard/verilog/sim/__init__.py +0 -0
- switchboard/verilog/sim/auto_stop_sim.sv +25 -0
- switchboard/verilog/sim/perf_meas_sim.sv +97 -0
- switchboard/verilog/sim/queue_to_sb_sim.sv +176 -0
- switchboard/verilog/sim/queue_to_umi_sim.sv +66 -0
- switchboard/verilog/sim/sb_apb_m.sv +146 -0
- switchboard/verilog/sim/sb_axi_m.sv +199 -0
- switchboard/verilog/sim/sb_axil_m.sv +180 -0
- switchboard/verilog/sim/sb_axil_s.sv +180 -0
- switchboard/verilog/sim/sb_clk_gen.sv +89 -0
- switchboard/verilog/sim/sb_jtag_rbb_sim.sv +148 -0
- switchboard/verilog/sim/sb_rx_sim.sv +55 -0
- switchboard/verilog/sim/sb_to_queue_sim.sv +196 -0
- switchboard/verilog/sim/sb_tx_sim.sv +55 -0
- switchboard/verilog/sim/switchboard_sim.py +49 -0
- switchboard/verilog/sim/umi_rx_sim.sv +61 -0
- switchboard/verilog/sim/umi_to_queue_sim.sv +66 -0
- switchboard/verilog/sim/umi_tx_sim.sv +61 -0
- switchboard/verilog/sim/xyce_intf.sv +67 -0
- switchboard/vpi/switchboard_vpi.cc +431 -0
- switchboard/vpi/xyce_vpi.cc +200 -0
- switchboard/warn.py +14 -0
- switchboard/xyce.py +27 -0
- switchboard_hw-0.3.0.dist-info/METADATA +303 -0
- switchboard_hw-0.3.0.dist-info/RECORD +99 -0
- switchboard_hw-0.3.0.dist-info/WHEEL +6 -0
- switchboard_hw-0.3.0.dist-info/entry_points.txt +6 -0
- switchboard_hw-0.3.0.dist-info/licenses/LICENSE +190 -0
- 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
|