edgefirst-ara2 0.1.3__tar.gz

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 (31) hide show
  1. edgefirst_ara2-0.1.3/Cargo.toml +41 -0
  2. edgefirst_ara2-0.1.3/PKG-INFO +329 -0
  3. edgefirst_ara2-0.1.3/README.md +300 -0
  4. edgefirst_ara2-0.1.3/crates/ara2/Cargo.toml +35 -0
  5. edgefirst_ara2-0.1.3/crates/ara2/README.md +165 -0
  6. edgefirst_ara2-0.1.3/crates/ara2/benches/model_benchmark.rs +66 -0
  7. edgefirst_ara2-0.1.3/crates/ara2/src/dvm_metadata.rs +484 -0
  8. edgefirst_ara2-0.1.3/crates/ara2/src/endpoint.rs +229 -0
  9. edgefirst_ara2-0.1.3/crates/ara2/src/error.rs +124 -0
  10. edgefirst_ara2-0.1.3/crates/ara2/src/lib.rs +49 -0
  11. edgefirst_ara2-0.1.3/crates/ara2/src/model.rs +705 -0
  12. edgefirst_ara2-0.1.3/crates/ara2/src/session.rs +278 -0
  13. edgefirst_ara2-0.1.3/crates/ara2-py/Cargo.lock +2266 -0
  14. edgefirst_ara2-0.1.3/crates/ara2-py/Cargo.toml +30 -0
  15. edgefirst_ara2-0.1.3/crates/ara2-py/README.md +300 -0
  16. edgefirst_ara2-0.1.3/crates/ara2-py/edgefirst_ara2.pyi +716 -0
  17. edgefirst_ara2-0.1.3/crates/ara2-py/src/endpoint.rs +55 -0
  18. edgefirst_ara2-0.1.3/crates/ara2-py/src/error.rs +66 -0
  19. edgefirst_ara2-0.1.3/crates/ara2-py/src/lib.rs +66 -0
  20. edgefirst_ara2-0.1.3/crates/ara2-py/src/metadata.rs +393 -0
  21. edgefirst_ara2-0.1.3/crates/ara2-py/src/model.rs +466 -0
  22. edgefirst_ara2-0.1.3/crates/ara2-py/src/session.rs +108 -0
  23. edgefirst_ara2-0.1.3/crates/ara2-py/src/types.rs +316 -0
  24. edgefirst_ara2-0.1.3/crates/ara2-sys/Cargo.toml +15 -0
  25. edgefirst_ara2-0.1.3/crates/ara2-sys/README.md +27 -0
  26. edgefirst_ara2-0.1.3/crates/ara2-sys/dv_status_codes.h +187 -0
  27. edgefirst_ara2-0.1.3/crates/ara2-sys/dvapi.h +1014 -0
  28. edgefirst_ara2-0.1.3/crates/ara2-sys/src/ffi.rs +2528 -0
  29. edgefirst_ara2-0.1.3/crates/ara2-sys/src/lib.rs +9 -0
  30. edgefirst_ara2-0.1.3/crates/ara2-sys/update.sh +3 -0
  31. edgefirst_ara2-0.1.3/pyproject.toml +44 -0
@@ -0,0 +1,41 @@
1
+ [workspace]
2
+ resolver = "3"
3
+ members = ["crates/*"]
4
+
5
+ [workspace.package]
6
+ description = "Rust client library for the Kinara ARA-2 neural network accelerator on NXP i.MX platforms"
7
+ authors = ["Au-Zone Technologies <support@au-zone.com>"]
8
+ homepage = "https://edgefirst.ai"
9
+ repository = "https://github.com/EdgeFirstAI/ara2-rs"
10
+ readme = "README.md"
11
+ license = "Apache-2.0"
12
+ version = "0.1.3"
13
+ edition = "2024"
14
+
15
+ [workspace.dependencies]
16
+ ara2 = { version = "0.1.3", path = "crates/ara2" }
17
+ ara2-sys = { version = "0.1.3", path = "crates/ara2-sys" }
18
+ criterion = "0.7.0"
19
+ edgefirst-hal = "0.13.0"
20
+ image = { version = "0.25.8", default-features = false, features = [
21
+ "jpeg",
22
+ "png",
23
+ "rayon",
24
+ ] }
25
+ libloading = "0.8.9"
26
+ log = { version = "0.4.28", features = ["release_max_level_debug"] }
27
+ ndarray = { version = "0.16.1", features = ["rayon"] }
28
+ numpy = { version = "0.24", default-features = false }
29
+ pyo3 = "0.24"
30
+ pyo3-build-config = "0.24"
31
+
32
+ [profile.release]
33
+ opt-level = 3
34
+ lto = true
35
+ strip = true
36
+ debug = false
37
+
38
+ [profile.profiling]
39
+ inherits = "release"
40
+ strip = false
41
+ debug = true
@@ -0,0 +1,329 @@
1
+ Metadata-Version: 2.4
2
+ Name: edgefirst-ara2
3
+ Version: 0.1.3
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: Apache Software License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Rust
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Requires-Dist: numpy>=1.24
15
+ Requires-Dist: edgefirst-hal ; extra == 'hal'
16
+ Provides-Extra: hal
17
+ Summary: Python bindings for the ARA-2 neural accelerator client library
18
+ Keywords: ara2,neural-network,accelerator,inference,edgefirst
19
+ Home-Page: https://edgefirst.ai
20
+ Author-email: Au-Zone Technologies <support@au-zone.com>
21
+ License: Apache-2.0
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
24
+ Project-URL: Documentation, https://github.com/EdgeFirstAI/ara2-rs#readme
25
+ Project-URL: Homepage, https://edgefirst.ai
26
+ Project-URL: Issues, https://github.com/EdgeFirstAI/ara2-rs/issues
27
+ Project-URL: Repository, https://github.com/EdgeFirstAI/ara2-rs
28
+
29
+ # Python Bindings for ARA-2
30
+
31
+ Python bindings for the ARA-2 neural network accelerator client library,
32
+ providing efficient NPU inference from Python via a proxy service running
33
+ on NXP i.MX platforms with Kinara ARA-2 hardware.
34
+
35
+ Published to PyPI as [`edgefirst-ara2`](https://pypi.org/project/edgefirst-ara2/).
36
+
37
+ ## Architecture
38
+
39
+ ```
40
+ Python Application ──(UNIX/TCP socket)──▶ ara2-proxy ──(PCIe)──▶ ARA-2 NPU
41
+ │ (system service) (Kinara hardware)
42
+
43
+ edgefirst-hal ──(DMA-BUF fd)──▶ GPU preprocessing (zero-copy)
44
+ ```
45
+
46
+ Your Python code connects to the `ara2-proxy` system service (not directly
47
+ to the hardware). The proxy manages device access and must be running before
48
+ your application starts.
49
+
50
+ ## Installation
51
+
52
+ ### From PyPI
53
+
54
+ ```bash
55
+ pip install edgefirst-ara2
56
+ ```
57
+
58
+ For zero-copy preprocessing with edgefirst-hal:
59
+
60
+ ```bash
61
+ pip install edgefirst-ara2[hal]
62
+ ```
63
+
64
+ ### Prerequisites for Development
65
+
66
+ - Python 3.11 or higher
67
+ - Rust stable toolchain (edition 2024)
68
+ - maturin (`pip install maturin`)
69
+ - ARA-2 client library (`libaraclient.so.1`)
70
+
71
+ ### Development Install
72
+
73
+ ```bash
74
+ cd crates/ara2-py
75
+ maturin develop --release --features abi3
76
+ ```
77
+
78
+ ## Quick Start
79
+
80
+ ```python
81
+ import edgefirst_ara2
82
+
83
+ # Connect to ARA-2 proxy
84
+ session = edgefirst_ara2.Session.create_via_unix_socket("/var/run/ara2.sock")
85
+
86
+ # Get version information
87
+ versions = session.versions()
88
+ print(f"Proxy version: {versions['proxy']}")
89
+
90
+ # List endpoints
91
+ endpoints = session.list_endpoints()
92
+ print(f"Found {len(endpoints)} endpoints")
93
+
94
+ # Check endpoint status
95
+ for endpoint in endpoints:
96
+ state = endpoint.check_status()
97
+ stats = endpoint.dram_statistics()
98
+ print(f"State: {state}, Free DRAM: {stats.free_size / stats.dram_size * 100:.1f}%")
99
+ ```
100
+
101
+ ## Inference with numpy
102
+
103
+ ```python
104
+ import numpy as np
105
+ import edgefirst_ara2
106
+
107
+ session = edgefirst_ara2.Session.create_via_unix_socket("/var/run/ara2.sock")
108
+ endpoints = session.list_endpoints()
109
+ model = endpoints[0].load_model("model.dvm")
110
+
111
+ # Allocate tensors and run inference
112
+ model.allocate_tensors()
113
+ input_data = np.zeros(model.input_size(0), dtype=np.uint8)
114
+ model.set_input_tensor(0, input_data)
115
+ timing = model.run()
116
+
117
+ print(f"Inference: {timing.run_time_us} us")
118
+ output = model.get_output_tensor(0)
119
+ dequantized = model.dequantize(0)
120
+ ```
121
+
122
+ ## Zero-Copy DMA-BUF Pipeline
123
+
124
+ For maximum throughput, use DMA-BUF tensors with
125
+ [edgefirst-hal](https://pypi.org/project/edgefirst-hal/) for GPU-accelerated
126
+ preprocessing. This eliminates CPU memory copies between preprocessing and
127
+ inference:
128
+
129
+ | Path | CPU copies | Flow |
130
+ |------|-----------|------|
131
+ | Standard (numpy) | 2 | numpy → shared memory → NPU |
132
+ | DMA-BUF | 0 | GPU writes directly to NPU input buffer |
133
+
134
+ **How it works:** `allocate_tensors("dma")` allocates the model's input tensor
135
+ in a DMA-BUF — a Linux kernel buffer accessible by multiple hardware devices.
136
+ `input_tensor_fd(0)` returns a file descriptor to that buffer. You pass this
137
+ FD to `edgefirst_hal.import_image()`, which maps it as a GPU image surface.
138
+ The GPU writes the preprocessed frame directly into the NPU's input buffer —
139
+ no CPU copies involved.
140
+
141
+ ```python
142
+ import os
143
+ import edgefirst_ara2 as ara2
144
+ import edgefirst_hal as hal
145
+
146
+ session = ara2.Session.create_via_unix_socket(ara2.DEFAULT_SOCKET)
147
+ endpoint = session.list_endpoints()[0]
148
+
149
+ with endpoint.load_model("yolov8s.dvm") as model:
150
+ model.allocate_tensors("dma") # Must use "dma" for tensor FD access
151
+
152
+ # Get DMA-BUF FD for the model's input tensor
153
+ input_fd = model.input_tensor_fd(0)
154
+ c, h, w = model.input_shape(0)
155
+ try:
156
+ # Import as PlanarRgb (CHW layout) to match ARA-2 tensor format
157
+ dst = hal.import_image(input_fd, w, h, hal.PixelFormat.PlanarRgb)
158
+ finally:
159
+ os.close(input_fd) # FD duplicated by import_image; close original
160
+
161
+ # GPU-accelerated convert: camera frame -> model input (zero CPU copies)
162
+ processor = hal.ImageProcessor()
163
+ src = hal.load_image("image.jpg", format=hal.PixelFormat.Rgba, mem=hal.TensorMemory.DMA)
164
+ processor.convert(src, dst)
165
+
166
+ # Run inference — NPU reads from the same DMA-BUF
167
+ timing = model.run()
168
+ print(f"Inference: {timing.run_time_us} us")
169
+ ```
170
+
171
+ ## Performance
172
+
173
+ Benchmarked on NXP i.MX 8M Plus + ARA-2 with YOLOv8n (640x640).
174
+ The Python API adds minimal overhead over native Rust thanks to DMA-BUF
175
+ zero-copy — GPU and NPU operate on the same physical memory buffers.
176
+
177
+ | Stage | Rust | Python | Overhead |
178
+ |-------|------|--------|----------|
179
+ | GPU preprocess (RGBA → CHW) | 6.35 ms | 6.37 ms | +0.02 ms |
180
+ | NPU inference (wall clock) | 8.95 ms | 9.13 ms | +0.18 ms |
181
+ | &nbsp;&nbsp;NPU execution | 3.33 ms | 3.33 ms | — |
182
+ | &nbsp;&nbsp;DMA input upload | 2.21 ms | 2.20 ms | — |
183
+ | &nbsp;&nbsp;DMA output download | 1.96 ms | 1.96 ms | — |
184
+ | Postprocess (decode + NMS) | 1.41 ms | 2.53 ms | +1.12 ms |
185
+ | **Total pipeline** | **16.71 ms** | **18.03 ms** | **+1.32 ms** |
186
+ | **Throughput** | **59.9 FPS** | **55.5 FPS** | |
187
+
188
+ > Steady-state mean over 20 iterations. Python overhead is in postprocessing
189
+ > (numpy array marshalling). GPU preprocessing and NPU inference are identical.
190
+
191
+ Run the benchmark yourself:
192
+
193
+ ```bash
194
+ python examples/yolov8.py model.dvm image.jpg --benchmark 20
195
+ ```
196
+
197
+ ## DVM Metadata
198
+
199
+ Read model metadata without loading onto the NPU:
200
+
201
+ ```python
202
+ import edgefirst_ara2
203
+
204
+ metadata = edgefirst_ara2.read_metadata("model.dvm")
205
+ if metadata:
206
+ print(f"Task: {metadata.task}")
207
+ print(f"Classes: {metadata.classes}")
208
+ if metadata.compilation and metadata.compilation.ppa:
209
+ print(f"IPS: {metadata.compilation.ppa.ips}")
210
+
211
+ labels = edgefirst_ara2.read_labels("model.dvm")
212
+ ```
213
+
214
+ ## API Reference
215
+
216
+ ### Session
217
+
218
+ Connection to the ARA-2 proxy service.
219
+
220
+ **Static Methods:**
221
+ - `create_via_unix_socket(socket_path: str) -> Session`
222
+ - `create_via_tcp_ipv4_socket(ip: str, port: int) -> Session`
223
+
224
+ **Methods:**
225
+ - `versions() -> dict[str, str]` - Get component versions
226
+ - `list_endpoints() -> list[Endpoint]` - List available endpoints
227
+
228
+ **Properties:**
229
+ - `socket_type: str` - "unix" or "tcp"
230
+
231
+ ### Endpoint
232
+
233
+ Represents an ARA-2 accelerator device.
234
+
235
+ **Methods:**
236
+ - `check_status() -> State` - Get device state
237
+ - `dram_statistics() -> DramStatistics` - Get memory usage
238
+ - `load_model(model_path: str) -> Model` - Load a .dvm model
239
+
240
+ ### Model
241
+
242
+ Loaded neural network model.
243
+
244
+ **Lifecycle:**
245
+ - `allocate_tensors(memory: str | None = None)` - Allocate tensors ("dma", "shm", "mem", or None)
246
+ - `set_timeout_ms(timeout_ms: int)` - Set inference timeout
247
+ - `run() -> ModelTiming` - Execute inference
248
+
249
+ **Tensor I/O (numpy):**
250
+ - `set_input_tensor(index: int, data: np.ndarray)` - Copy data into input
251
+ - `get_output_tensor(index: int) -> np.ndarray` - Copy output data out
252
+ - `dequantize(index: int) -> np.ndarray` - Dequantize output to float32
253
+
254
+ **DMA-BUF Zero-Copy:**
255
+ - `input_tensor_fd(index: int) -> int` - Get input tensor FD
256
+ - `output_tensor_fd(index: int) -> int` - Get output tensor FD
257
+ - `input_tensor_memory(index: int) -> str` - Input memory type
258
+ - `output_tensor_memory(index: int) -> str` - Output memory type
259
+
260
+ **Introspection:**
261
+ - `n_inputs: int`, `n_outputs: int` - Tensor counts
262
+ - `input_shape(i) -> (C, H, W)`, `output_shape(i) -> (C, H, W)`
263
+ - `input_size(i) -> int`, `output_size(i) -> int` - Size in bytes
264
+ - `input_bpp(i) -> int`, `output_bpp(i) -> int` - Bytes per element
265
+ - `input_info(i) -> InputTensorInfo`, `output_info(i) -> OutputTensorInfo`
266
+ - `input_quants(i) -> InputQuantization`, `output_quants(i) -> OutputQuantization`
267
+
268
+ ### Metadata Functions
269
+
270
+ - `read_metadata(path: str) -> DvmMetadata | None`
271
+ - `read_labels(path: str) -> list[str]`
272
+ - `has_metadata(path: str) -> bool`
273
+
274
+ ### Supporting Types
275
+
276
+ - **State** (enum): Init, Idle, Active, ActiveSlow, ActiveBoosted, ThermalInactive, ThermalUnknown, Inactive, Fault
277
+ - **ModelOutputType** (enum): Classification, Detection, SemanticSegmentation, Raw
278
+ - **DramStatistics**: dram_size, free_size, model_occupancy_size, ...
279
+ - **ModelTiming**: run_time_us, input_time_us, output_time_us
280
+ - **InputQuantization**: qn, scale, mean, is_signed
281
+ - **OutputQuantization**: qn, scale, offset, is_signed
282
+
283
+ ### Exceptions
284
+
285
+ ```
286
+ Ara2Error (RuntimeError)
287
+ +-- LibraryError - libaraclient.so loading failures
288
+ +-- HardwareError - NPU faults, endpoint errors
289
+ +-- ProxyError - Proxy connection failures
290
+ +-- ModelError - Model load/inference failures
291
+ +-- TensorError - Tensor allocation, DMA-BUF errors
292
+ +-- MetadataError - DVM metadata parsing errors
293
+ ```
294
+
295
+ ## Building Wheels
296
+
297
+ ```bash
298
+ cd crates/ara2-py
299
+ maturin build --release --features abi3
300
+ ```
301
+
302
+ Wheels are created in `target/wheels/`.
303
+
304
+ ## Stable ABI
305
+
306
+ The bindings use PyO3's stable ABI (`abi3-py311`):
307
+ - A single wheel works across Python 3.11, 3.12, 3.13, and future versions
308
+ - Minimum supported Python version is 3.11
309
+
310
+ ## Troubleshooting
311
+
312
+ ### "libaraclient.so.1 not found"
313
+
314
+ ```bash
315
+ export LD_LIBRARY_PATH=/path/to/ara2/lib:$LD_LIBRARY_PATH
316
+ ```
317
+
318
+ ### Verify Installation
319
+
320
+ ```bash
321
+ python -c "import edgefirst_ara2; print(edgefirst_ara2.__version__)"
322
+ ```
323
+
324
+ ## License
325
+
326
+ Licensed under the Apache License 2.0.
327
+
328
+ Copyright 2025 Au-Zone Technologies. All Rights Reserved.
329
+
@@ -0,0 +1,300 @@
1
+ # Python Bindings for ARA-2
2
+
3
+ Python bindings for the ARA-2 neural network accelerator client library,
4
+ providing efficient NPU inference from Python via a proxy service running
5
+ on NXP i.MX platforms with Kinara ARA-2 hardware.
6
+
7
+ Published to PyPI as [`edgefirst-ara2`](https://pypi.org/project/edgefirst-ara2/).
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ Python Application ──(UNIX/TCP socket)──▶ ara2-proxy ──(PCIe)──▶ ARA-2 NPU
13
+ │ (system service) (Kinara hardware)
14
+
15
+ edgefirst-hal ──(DMA-BUF fd)──▶ GPU preprocessing (zero-copy)
16
+ ```
17
+
18
+ Your Python code connects to the `ara2-proxy` system service (not directly
19
+ to the hardware). The proxy manages device access and must be running before
20
+ your application starts.
21
+
22
+ ## Installation
23
+
24
+ ### From PyPI
25
+
26
+ ```bash
27
+ pip install edgefirst-ara2
28
+ ```
29
+
30
+ For zero-copy preprocessing with edgefirst-hal:
31
+
32
+ ```bash
33
+ pip install edgefirst-ara2[hal]
34
+ ```
35
+
36
+ ### Prerequisites for Development
37
+
38
+ - Python 3.11 or higher
39
+ - Rust stable toolchain (edition 2024)
40
+ - maturin (`pip install maturin`)
41
+ - ARA-2 client library (`libaraclient.so.1`)
42
+
43
+ ### Development Install
44
+
45
+ ```bash
46
+ cd crates/ara2-py
47
+ maturin develop --release --features abi3
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ import edgefirst_ara2
54
+
55
+ # Connect to ARA-2 proxy
56
+ session = edgefirst_ara2.Session.create_via_unix_socket("/var/run/ara2.sock")
57
+
58
+ # Get version information
59
+ versions = session.versions()
60
+ print(f"Proxy version: {versions['proxy']}")
61
+
62
+ # List endpoints
63
+ endpoints = session.list_endpoints()
64
+ print(f"Found {len(endpoints)} endpoints")
65
+
66
+ # Check endpoint status
67
+ for endpoint in endpoints:
68
+ state = endpoint.check_status()
69
+ stats = endpoint.dram_statistics()
70
+ print(f"State: {state}, Free DRAM: {stats.free_size / stats.dram_size * 100:.1f}%")
71
+ ```
72
+
73
+ ## Inference with numpy
74
+
75
+ ```python
76
+ import numpy as np
77
+ import edgefirst_ara2
78
+
79
+ session = edgefirst_ara2.Session.create_via_unix_socket("/var/run/ara2.sock")
80
+ endpoints = session.list_endpoints()
81
+ model = endpoints[0].load_model("model.dvm")
82
+
83
+ # Allocate tensors and run inference
84
+ model.allocate_tensors()
85
+ input_data = np.zeros(model.input_size(0), dtype=np.uint8)
86
+ model.set_input_tensor(0, input_data)
87
+ timing = model.run()
88
+
89
+ print(f"Inference: {timing.run_time_us} us")
90
+ output = model.get_output_tensor(0)
91
+ dequantized = model.dequantize(0)
92
+ ```
93
+
94
+ ## Zero-Copy DMA-BUF Pipeline
95
+
96
+ For maximum throughput, use DMA-BUF tensors with
97
+ [edgefirst-hal](https://pypi.org/project/edgefirst-hal/) for GPU-accelerated
98
+ preprocessing. This eliminates CPU memory copies between preprocessing and
99
+ inference:
100
+
101
+ | Path | CPU copies | Flow |
102
+ |------|-----------|------|
103
+ | Standard (numpy) | 2 | numpy → shared memory → NPU |
104
+ | DMA-BUF | 0 | GPU writes directly to NPU input buffer |
105
+
106
+ **How it works:** `allocate_tensors("dma")` allocates the model's input tensor
107
+ in a DMA-BUF — a Linux kernel buffer accessible by multiple hardware devices.
108
+ `input_tensor_fd(0)` returns a file descriptor to that buffer. You pass this
109
+ FD to `edgefirst_hal.import_image()`, which maps it as a GPU image surface.
110
+ The GPU writes the preprocessed frame directly into the NPU's input buffer —
111
+ no CPU copies involved.
112
+
113
+ ```python
114
+ import os
115
+ import edgefirst_ara2 as ara2
116
+ import edgefirst_hal as hal
117
+
118
+ session = ara2.Session.create_via_unix_socket(ara2.DEFAULT_SOCKET)
119
+ endpoint = session.list_endpoints()[0]
120
+
121
+ with endpoint.load_model("yolov8s.dvm") as model:
122
+ model.allocate_tensors("dma") # Must use "dma" for tensor FD access
123
+
124
+ # Get DMA-BUF FD for the model's input tensor
125
+ input_fd = model.input_tensor_fd(0)
126
+ c, h, w = model.input_shape(0)
127
+ try:
128
+ # Import as PlanarRgb (CHW layout) to match ARA-2 tensor format
129
+ dst = hal.import_image(input_fd, w, h, hal.PixelFormat.PlanarRgb)
130
+ finally:
131
+ os.close(input_fd) # FD duplicated by import_image; close original
132
+
133
+ # GPU-accelerated convert: camera frame -> model input (zero CPU copies)
134
+ processor = hal.ImageProcessor()
135
+ src = hal.load_image("image.jpg", format=hal.PixelFormat.Rgba, mem=hal.TensorMemory.DMA)
136
+ processor.convert(src, dst)
137
+
138
+ # Run inference — NPU reads from the same DMA-BUF
139
+ timing = model.run()
140
+ print(f"Inference: {timing.run_time_us} us")
141
+ ```
142
+
143
+ ## Performance
144
+
145
+ Benchmarked on NXP i.MX 8M Plus + ARA-2 with YOLOv8n (640x640).
146
+ The Python API adds minimal overhead over native Rust thanks to DMA-BUF
147
+ zero-copy — GPU and NPU operate on the same physical memory buffers.
148
+
149
+ | Stage | Rust | Python | Overhead |
150
+ |-------|------|--------|----------|
151
+ | GPU preprocess (RGBA → CHW) | 6.35 ms | 6.37 ms | +0.02 ms |
152
+ | NPU inference (wall clock) | 8.95 ms | 9.13 ms | +0.18 ms |
153
+ | &nbsp;&nbsp;NPU execution | 3.33 ms | 3.33 ms | — |
154
+ | &nbsp;&nbsp;DMA input upload | 2.21 ms | 2.20 ms | — |
155
+ | &nbsp;&nbsp;DMA output download | 1.96 ms | 1.96 ms | — |
156
+ | Postprocess (decode + NMS) | 1.41 ms | 2.53 ms | +1.12 ms |
157
+ | **Total pipeline** | **16.71 ms** | **18.03 ms** | **+1.32 ms** |
158
+ | **Throughput** | **59.9 FPS** | **55.5 FPS** | |
159
+
160
+ > Steady-state mean over 20 iterations. Python overhead is in postprocessing
161
+ > (numpy array marshalling). GPU preprocessing and NPU inference are identical.
162
+
163
+ Run the benchmark yourself:
164
+
165
+ ```bash
166
+ python examples/yolov8.py model.dvm image.jpg --benchmark 20
167
+ ```
168
+
169
+ ## DVM Metadata
170
+
171
+ Read model metadata without loading onto the NPU:
172
+
173
+ ```python
174
+ import edgefirst_ara2
175
+
176
+ metadata = edgefirst_ara2.read_metadata("model.dvm")
177
+ if metadata:
178
+ print(f"Task: {metadata.task}")
179
+ print(f"Classes: {metadata.classes}")
180
+ if metadata.compilation and metadata.compilation.ppa:
181
+ print(f"IPS: {metadata.compilation.ppa.ips}")
182
+
183
+ labels = edgefirst_ara2.read_labels("model.dvm")
184
+ ```
185
+
186
+ ## API Reference
187
+
188
+ ### Session
189
+
190
+ Connection to the ARA-2 proxy service.
191
+
192
+ **Static Methods:**
193
+ - `create_via_unix_socket(socket_path: str) -> Session`
194
+ - `create_via_tcp_ipv4_socket(ip: str, port: int) -> Session`
195
+
196
+ **Methods:**
197
+ - `versions() -> dict[str, str]` - Get component versions
198
+ - `list_endpoints() -> list[Endpoint]` - List available endpoints
199
+
200
+ **Properties:**
201
+ - `socket_type: str` - "unix" or "tcp"
202
+
203
+ ### Endpoint
204
+
205
+ Represents an ARA-2 accelerator device.
206
+
207
+ **Methods:**
208
+ - `check_status() -> State` - Get device state
209
+ - `dram_statistics() -> DramStatistics` - Get memory usage
210
+ - `load_model(model_path: str) -> Model` - Load a .dvm model
211
+
212
+ ### Model
213
+
214
+ Loaded neural network model.
215
+
216
+ **Lifecycle:**
217
+ - `allocate_tensors(memory: str | None = None)` - Allocate tensors ("dma", "shm", "mem", or None)
218
+ - `set_timeout_ms(timeout_ms: int)` - Set inference timeout
219
+ - `run() -> ModelTiming` - Execute inference
220
+
221
+ **Tensor I/O (numpy):**
222
+ - `set_input_tensor(index: int, data: np.ndarray)` - Copy data into input
223
+ - `get_output_tensor(index: int) -> np.ndarray` - Copy output data out
224
+ - `dequantize(index: int) -> np.ndarray` - Dequantize output to float32
225
+
226
+ **DMA-BUF Zero-Copy:**
227
+ - `input_tensor_fd(index: int) -> int` - Get input tensor FD
228
+ - `output_tensor_fd(index: int) -> int` - Get output tensor FD
229
+ - `input_tensor_memory(index: int) -> str` - Input memory type
230
+ - `output_tensor_memory(index: int) -> str` - Output memory type
231
+
232
+ **Introspection:**
233
+ - `n_inputs: int`, `n_outputs: int` - Tensor counts
234
+ - `input_shape(i) -> (C, H, W)`, `output_shape(i) -> (C, H, W)`
235
+ - `input_size(i) -> int`, `output_size(i) -> int` - Size in bytes
236
+ - `input_bpp(i) -> int`, `output_bpp(i) -> int` - Bytes per element
237
+ - `input_info(i) -> InputTensorInfo`, `output_info(i) -> OutputTensorInfo`
238
+ - `input_quants(i) -> InputQuantization`, `output_quants(i) -> OutputQuantization`
239
+
240
+ ### Metadata Functions
241
+
242
+ - `read_metadata(path: str) -> DvmMetadata | None`
243
+ - `read_labels(path: str) -> list[str]`
244
+ - `has_metadata(path: str) -> bool`
245
+
246
+ ### Supporting Types
247
+
248
+ - **State** (enum): Init, Idle, Active, ActiveSlow, ActiveBoosted, ThermalInactive, ThermalUnknown, Inactive, Fault
249
+ - **ModelOutputType** (enum): Classification, Detection, SemanticSegmentation, Raw
250
+ - **DramStatistics**: dram_size, free_size, model_occupancy_size, ...
251
+ - **ModelTiming**: run_time_us, input_time_us, output_time_us
252
+ - **InputQuantization**: qn, scale, mean, is_signed
253
+ - **OutputQuantization**: qn, scale, offset, is_signed
254
+
255
+ ### Exceptions
256
+
257
+ ```
258
+ Ara2Error (RuntimeError)
259
+ +-- LibraryError - libaraclient.so loading failures
260
+ +-- HardwareError - NPU faults, endpoint errors
261
+ +-- ProxyError - Proxy connection failures
262
+ +-- ModelError - Model load/inference failures
263
+ +-- TensorError - Tensor allocation, DMA-BUF errors
264
+ +-- MetadataError - DVM metadata parsing errors
265
+ ```
266
+
267
+ ## Building Wheels
268
+
269
+ ```bash
270
+ cd crates/ara2-py
271
+ maturin build --release --features abi3
272
+ ```
273
+
274
+ Wheels are created in `target/wheels/`.
275
+
276
+ ## Stable ABI
277
+
278
+ The bindings use PyO3's stable ABI (`abi3-py311`):
279
+ - A single wheel works across Python 3.11, 3.12, 3.13, and future versions
280
+ - Minimum supported Python version is 3.11
281
+
282
+ ## Troubleshooting
283
+
284
+ ### "libaraclient.so.1 not found"
285
+
286
+ ```bash
287
+ export LD_LIBRARY_PATH=/path/to/ara2/lib:$LD_LIBRARY_PATH
288
+ ```
289
+
290
+ ### Verify Installation
291
+
292
+ ```bash
293
+ python -c "import edgefirst_ara2; print(edgefirst_ara2.__version__)"
294
+ ```
295
+
296
+ ## License
297
+
298
+ Licensed under the Apache License 2.0.
299
+
300
+ Copyright 2025 Au-Zone Technologies. All Rights Reserved.
@@ -0,0 +1,35 @@
1
+ [package]
2
+ name = "ara2"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ authors.workspace = true
6
+ description.workspace = true
7
+ homepage.workspace = true
8
+ repository.workspace = true
9
+ license.workspace = true
10
+ readme = "README.md"
11
+ keywords = ["neural-network", "inference", "accelerator", "ara2", "edgefirst"]
12
+ categories = ["hardware-support", "science", "embedded"]
13
+ exclude = ["target/", "*.dvm", "*.onnx"]
14
+
15
+ [features]
16
+ default = ["hal"]
17
+ hal = ["edgefirst-hal", "image"]
18
+
19
+ [dependencies]
20
+ ara2-sys.workspace = true
21
+ edgefirst-hal = { workspace = true, optional = true }
22
+ image = { workspace = true, optional = true }
23
+ libloading.workspace = true
24
+ log.workspace = true
25
+ ndarray.workspace = true
26
+ serde = { version = "1.0", features = ["derive"] }
27
+ serde_json = "1.0"
28
+ zip = { version = "2.2", default-features = false }
29
+
30
+ [dev-dependencies]
31
+ criterion.workspace = true
32
+
33
+ [[bench]]
34
+ name = "model_benchmark"
35
+ harness = false