blob: 9d3335e29622c5f7b6f223f428470030d542586c [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "hal/SPI.h"
6
7#include <fcntl.h>
8#include <linux/spi/spidev.h>
9#include <sys/ioctl.h>
10#include <unistd.h>
11
12#include <array>
13#include <atomic>
Austin Schuh812d0d12021-11-04 20:16:48 -070014#include <cstdio>
Brian Silverman8fce7482020-01-05 13:18:21 -080015#include <cstring>
16
Austin Schuh812d0d12021-11-04 20:16:48 -070017#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080019
20#include "DigitalInternal.h"
21#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070022#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080023#include "hal/DIO.h"
24#include "hal/HAL.h"
25#include "hal/handles/HandlesInternal.h"
26
27using namespace hal;
28
29static int32_t m_spiCS0Handle{0};
30static int32_t m_spiCS1Handle{0};
31static int32_t m_spiCS2Handle{0};
32static int32_t m_spiCS3Handle{0};
33static int32_t m_spiMXPHandle{0};
34
35static constexpr int32_t kSpiMaxHandles = 5;
36
37// Indices 0-3 are for onboard CS0-CS2. Index 4 is for MXP.
38static std::array<wpi::mutex, kSpiMaxHandles> spiHandleMutexes;
39static std::array<wpi::mutex, kSpiMaxHandles> spiApiMutexes;
40static std::array<wpi::mutex, kSpiMaxHandles> spiAccumulatorMutexes;
41
42// MXP SPI does not count towards this
43static std::atomic<int32_t> spiPortCount{0};
44
45static HAL_DigitalHandle digitalHandles[9]{HAL_kInvalidHandle};
46
47static wpi::mutex spiAutoMutex;
48static int32_t spiAutoPort = kSpiMaxHandles;
49static std::atomic_bool spiAutoRunning{false};
50static std::unique_ptr<tDMAManager> spiAutoDMA;
51
52static bool SPIInUseByAuto(HAL_SPIPort port) {
53 // SPI engine conflicts with any other chip selects on the same SPI device.
54 // There are two SPI devices: one for ports 0-3 (onboard), the other for port
55 // 4 (MXP).
Austin Schuh812d0d12021-11-04 20:16:48 -070056 if (!spiAutoRunning) {
57 return false;
58 }
Brian Silverman8fce7482020-01-05 13:18:21 -080059 std::scoped_lock lock(spiAutoMutex);
60 return (spiAutoPort >= 0 && spiAutoPort <= 3 && port >= 0 && port <= 3) ||
61 (spiAutoPort == 4 && port == 4);
62}
63
Austin Schuh812d0d12021-11-04 20:16:48 -070064namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080065void InitializeSPI() {}
Austin Schuh812d0d12021-11-04 20:16:48 -070066} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080067
68extern "C" {
69
70static void CommonSPIPortInit(int32_t* status) {
71 // All false cases will set
72 if (spiPortCount.fetch_add(1) == 0) {
73 // Have not been initialized yet
74 initializeDigital(status);
Austin Schuh812d0d12021-11-04 20:16:48 -070075 if (*status != 0) {
76 return;
77 }
Brian Silverman8fce7482020-01-05 13:18:21 -080078 // MISO
79 if ((digitalHandles[3] = HAL_InitializeDIOPort(createPortHandleForSPI(29),
Austin Schuh812d0d12021-11-04 20:16:48 -070080 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -080081 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -070082 std::puts("Failed to allocate DIO 29 (MISO)");
Brian Silverman8fce7482020-01-05 13:18:21 -080083 return;
84 }
85 // MOSI
86 if ((digitalHandles[4] = HAL_InitializeDIOPort(createPortHandleForSPI(30),
Austin Schuh812d0d12021-11-04 20:16:48 -070087 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -080088 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -070089 std::puts("Failed to allocate DIO 30 (MOSI)");
Brian Silverman8fce7482020-01-05 13:18:21 -080090 HAL_FreeDIOPort(digitalHandles[3]); // free the first port allocated
91 return;
92 }
93 }
94}
95
96static void CommonSPIPortFree(void) {
97 if (spiPortCount.fetch_sub(1) == 1) {
98 // Clean up SPI Handles
99 HAL_FreeDIOPort(digitalHandles[3]);
100 HAL_FreeDIOPort(digitalHandles[4]);
101 }
102}
103
104void HAL_InitializeSPI(HAL_SPIPort port, int32_t* status) {
105 hal::init::CheckInit();
106 if (port < 0 || port >= kSpiMaxHandles) {
107 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700108 hal::SetLastError(
109 status,
110 fmt::format("Serial port must be between 0 and {}. Requested {}",
111 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800112 return;
113 }
114
115 int handle;
Austin Schuh812d0d12021-11-04 20:16:48 -0700116 if (HAL_GetSPIHandle(port) != 0) {
117 return;
118 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800119 switch (port) {
120 case HAL_SPI_kOnboardCS0:
121 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700122 if (*status != 0) {
123 return;
124 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800125 // CS0 is not a DIO port, so nothing to allocate
126 handle = open("/dev/spidev0.0", O_RDWR);
127 if (handle < 0) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800128 fmt::print("Failed to open SPI port {}: {}\n",
129 static_cast<int32_t>(port), std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800130 CommonSPIPortFree();
131 return;
132 }
133 HAL_SetSPIHandle(HAL_SPI_kOnboardCS0, handle);
134 break;
135 case HAL_SPI_kOnboardCS1:
136 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700137 if (*status != 0) {
138 return;
139 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800140 // CS1, Allocate
141 if ((digitalHandles[0] = HAL_InitializeDIOPort(createPortHandleForSPI(26),
Austin Schuh812d0d12021-11-04 20:16:48 -0700142 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800143 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700144 std::puts("Failed to allocate DIO 26 (CS1)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 CommonSPIPortFree();
146 return;
147 }
148 handle = open("/dev/spidev0.1", O_RDWR);
149 if (handle < 0) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800150 fmt::print("Failed to open SPI port {}: {}\n",
151 static_cast<int32_t>(port), std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800152 CommonSPIPortFree();
153 HAL_FreeDIOPort(digitalHandles[0]);
154 return;
155 }
156 HAL_SetSPIHandle(HAL_SPI_kOnboardCS1, handle);
157 break;
158 case HAL_SPI_kOnboardCS2:
159 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700160 if (*status != 0) {
161 return;
162 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800163 // CS2, Allocate
164 if ((digitalHandles[1] = HAL_InitializeDIOPort(createPortHandleForSPI(27),
Austin Schuh812d0d12021-11-04 20:16:48 -0700165 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800166 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700167 std::puts("Failed to allocate DIO 27 (CS2)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800168 CommonSPIPortFree();
169 return;
170 }
171 handle = open("/dev/spidev0.2", O_RDWR);
172 if (handle < 0) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800173 fmt::print("Failed to open SPI port {}: {}\n",
174 static_cast<int32_t>(port), std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800175 CommonSPIPortFree();
176 HAL_FreeDIOPort(digitalHandles[1]);
177 return;
178 }
179 HAL_SetSPIHandle(HAL_SPI_kOnboardCS2, handle);
180 break;
181 case HAL_SPI_kOnboardCS3:
182 CommonSPIPortInit(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700183 if (*status != 0) {
184 return;
185 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800186 // CS3, Allocate
187 if ((digitalHandles[2] = HAL_InitializeDIOPort(createPortHandleForSPI(28),
Austin Schuh812d0d12021-11-04 20:16:48 -0700188 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800189 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700190 std::puts("Failed to allocate DIO 28 (CS3)");
Brian Silverman8fce7482020-01-05 13:18:21 -0800191 CommonSPIPortFree();
192 return;
193 }
194 handle = open("/dev/spidev0.3", O_RDWR);
195 if (handle < 0) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800196 fmt::print("Failed to open SPI port {}: {}\n",
197 static_cast<int32_t>(port), std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800198 CommonSPIPortFree();
199 HAL_FreeDIOPort(digitalHandles[2]);
200 return;
201 }
202 HAL_SetSPIHandle(HAL_SPI_kOnboardCS3, handle);
203 break;
204 case HAL_SPI_kMXP:
205 initializeDigital(status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700206 if (*status != 0) {
207 return;
208 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800209 if ((digitalHandles[5] = HAL_InitializeDIOPort(createPortHandleForSPI(14),
Austin Schuh812d0d12021-11-04 20:16:48 -0700210 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800211 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700212 std::puts("Failed to allocate DIO 14");
Brian Silverman8fce7482020-01-05 13:18:21 -0800213 return;
214 }
215 if ((digitalHandles[6] = HAL_InitializeDIOPort(createPortHandleForSPI(15),
Austin Schuh812d0d12021-11-04 20:16:48 -0700216 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800217 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700218 std::puts("Failed to allocate DIO 15");
Brian Silverman8fce7482020-01-05 13:18:21 -0800219 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
220 return;
221 }
222 if ((digitalHandles[7] = HAL_InitializeDIOPort(createPortHandleForSPI(16),
Austin Schuh812d0d12021-11-04 20:16:48 -0700223 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800224 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700225 std::puts("Failed to allocate DIO 16");
Brian Silverman8fce7482020-01-05 13:18:21 -0800226 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
227 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
228 return;
229 }
230 if ((digitalHandles[8] = HAL_InitializeDIOPort(createPortHandleForSPI(17),
Austin Schuh812d0d12021-11-04 20:16:48 -0700231 false, nullptr, status)) ==
Brian Silverman8fce7482020-01-05 13:18:21 -0800232 HAL_kInvalidHandle) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700233 std::puts("Failed to allocate DIO 17");
Brian Silverman8fce7482020-01-05 13:18:21 -0800234 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
235 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
236 HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
237 return;
238 }
239 digitalSystem->writeEnableMXPSpecialFunction(
240 digitalSystem->readEnableMXPSpecialFunction(status) | 0x00F0, status);
241 handle = open("/dev/spidev1.0", O_RDWR);
242 if (handle < 0) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800243 fmt::print("Failed to open SPI port {}: {}\n",
244 static_cast<int32_t>(port), std::strerror(errno));
Brian Silverman8fce7482020-01-05 13:18:21 -0800245 HAL_FreeDIOPort(digitalHandles[5]); // free the first port allocated
246 HAL_FreeDIOPort(digitalHandles[6]); // free the second port allocated
247 HAL_FreeDIOPort(digitalHandles[7]); // free the third port allocated
248 HAL_FreeDIOPort(digitalHandles[8]); // free the fourth port allocated
249 return;
250 }
251 HAL_SetSPIHandle(HAL_SPI_kMXP, handle);
252 break;
253 default:
254 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700255 hal::SetLastError(
256 status, fmt::format("Invalid SPI port {}", static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800257 break;
258 }
259}
260
261int32_t HAL_TransactionSPI(HAL_SPIPort port, const uint8_t* dataToSend,
262 uint8_t* dataReceived, int32_t size) {
263 if (port < 0 || port >= kSpiMaxHandles) {
264 return -1;
265 }
266
Austin Schuh812d0d12021-11-04 20:16:48 -0700267 if (SPIInUseByAuto(port)) {
268 return -1;
269 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800270
271 struct spi_ioc_transfer xfer;
272 std::memset(&xfer, 0, sizeof(xfer));
273 xfer.tx_buf = (__u64)dataToSend;
274 xfer.rx_buf = (__u64)dataReceived;
275 xfer.len = size;
276
277 std::scoped_lock lock(spiApiMutexes[port]);
278 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
279}
280
281int32_t HAL_WriteSPI(HAL_SPIPort port, const uint8_t* dataToSend,
282 int32_t sendSize) {
283 if (port < 0 || port >= kSpiMaxHandles) {
284 return -1;
285 }
286
Austin Schuh812d0d12021-11-04 20:16:48 -0700287 if (SPIInUseByAuto(port)) {
288 return -1;
289 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800290
291 struct spi_ioc_transfer xfer;
292 std::memset(&xfer, 0, sizeof(xfer));
293 xfer.tx_buf = (__u64)dataToSend;
294 xfer.len = sendSize;
295
296 std::scoped_lock lock(spiApiMutexes[port]);
297 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
298}
299
300int32_t HAL_ReadSPI(HAL_SPIPort port, uint8_t* buffer, int32_t count) {
301 if (port < 0 || port >= kSpiMaxHandles) {
302 return -1;
303 }
304
Austin Schuh812d0d12021-11-04 20:16:48 -0700305 if (SPIInUseByAuto(port)) {
306 return -1;
307 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800308
309 struct spi_ioc_transfer xfer;
310 std::memset(&xfer, 0, sizeof(xfer));
311 xfer.rx_buf = (__u64)buffer;
312 xfer.len = count;
313
314 std::scoped_lock lock(spiApiMutexes[port]);
315 return ioctl(HAL_GetSPIHandle(port), SPI_IOC_MESSAGE(1), &xfer);
316}
317
318void HAL_CloseSPI(HAL_SPIPort port) {
319 if (port < 0 || port >= kSpiMaxHandles) {
320 return;
321 }
322
323 int32_t status = 0;
324 HAL_FreeSPIAuto(port, &status);
325
326 {
327 std::scoped_lock lock(spiApiMutexes[port]);
328 close(HAL_GetSPIHandle(port));
329 }
330
331 HAL_SetSPIHandle(port, 0);
332 if (port < 4) {
333 CommonSPIPortFree();
334 }
335
336 switch (port) {
337 // Case 0 does not need to do anything
338 case 1:
339 HAL_FreeDIOPort(digitalHandles[0]);
340 break;
341 case 2:
342 HAL_FreeDIOPort(digitalHandles[1]);
343 break;
344 case 3:
345 HAL_FreeDIOPort(digitalHandles[2]);
346 break;
347 case 4:
348 HAL_FreeDIOPort(digitalHandles[5]);
349 HAL_FreeDIOPort(digitalHandles[6]);
350 HAL_FreeDIOPort(digitalHandles[7]);
351 HAL_FreeDIOPort(digitalHandles[8]);
352 break;
353 default:
354 break;
355 }
356}
357
358void HAL_SetSPISpeed(HAL_SPIPort port, int32_t speed) {
359 if (port < 0 || port >= kSpiMaxHandles) {
360 return;
361 }
362
363 std::scoped_lock lock(spiApiMutexes[port]);
364 ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MAX_SPEED_HZ, &speed);
365}
366
James Kuszmaulcf324122023-01-14 14:07:17 -0800367void HAL_SetSPIMode(HAL_SPIPort port, HAL_SPIMode mode) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800368 if (port < 0 || port >= kSpiMaxHandles) {
369 return;
370 }
371
James Kuszmaulcf324122023-01-14 14:07:17 -0800372 uint8_t mode8 = mode & SPI_MODE_3;
Brian Silverman8fce7482020-01-05 13:18:21 -0800373
374 std::scoped_lock lock(spiApiMutexes[port]);
James Kuszmaulcf324122023-01-14 14:07:17 -0800375 ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode8);
376}
377
378HAL_SPIMode HAL_GetSPIMode(HAL_SPIPort port) {
379 if (port < 0 || port >= kSpiMaxHandles) {
380 return HAL_SPI_kMode0;
381 }
382
383 uint8_t mode8 = 0;
384
385 std::scoped_lock lock(spiApiMutexes[port]);
386 ioctl(HAL_GetSPIHandle(port), SPI_IOC_RD_MODE, &mode8);
387 return static_cast<HAL_SPIMode>(mode8 & SPI_MODE_3);
Brian Silverman8fce7482020-01-05 13:18:21 -0800388}
389
390void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
391 if (port < 0 || port >= kSpiMaxHandles) {
392 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700393 hal::SetLastError(
394 status,
395 fmt::format("Serial port must be between 0 and {}. Requested {}",
396 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800397 return;
398 }
399
400 std::scoped_lock lock(spiApiMutexes[port]);
401 if (port < 4) {
402 spiSystem->writeChipSelectActiveHigh_Hdr(
403 spiSystem->readChipSelectActiveHigh_Hdr(status) | (1 << port), status);
404 } else {
405 spiSystem->writeChipSelectActiveHigh_MXP(1, status);
406 }
407}
408
409void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {
410 if (port < 0 || port >= kSpiMaxHandles) {
411 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700412 hal::SetLastError(
413 status,
414 fmt::format("Serial port must be between 0 and {}. Requested {}",
415 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800416 return;
417 }
418
419 std::scoped_lock lock(spiApiMutexes[port]);
420 if (port < 4) {
421 spiSystem->writeChipSelectActiveHigh_Hdr(
422 spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1 << port), status);
423 } else {
424 spiSystem->writeChipSelectActiveHigh_MXP(0, status);
425 }
426}
427
428int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
429 if (port < 0 || port >= kSpiMaxHandles) {
430 return 0;
431 }
432
433 std::scoped_lock lock(spiHandleMutexes[port]);
434 switch (port) {
435 case 0:
436 return m_spiCS0Handle;
437 case 1:
438 return m_spiCS1Handle;
439 case 2:
440 return m_spiCS2Handle;
441 case 3:
442 return m_spiCS3Handle;
443 case 4:
444 return m_spiMXPHandle;
445 default:
446 return 0;
447 }
448}
449
450void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) {
451 if (port < 0 || port >= kSpiMaxHandles) {
452 return;
453 }
454
455 std::scoped_lock lock(spiHandleMutexes[port]);
456 switch (port) {
457 case 0:
458 m_spiCS0Handle = handle;
459 break;
460 case 1:
461 m_spiCS1Handle = handle;
462 break;
463 case 2:
464 m_spiCS2Handle = handle;
465 break;
466 case 3:
467 m_spiCS3Handle = handle;
468 break;
469 case 4:
470 m_spiMXPHandle = handle;
471 break;
472 default:
473 break;
474 }
475}
476
477void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) {
478 if (port < 0 || port >= kSpiMaxHandles) {
479 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700480 hal::SetLastError(
481 status,
482 fmt::format("Serial port must be between 0 and {}. Requested {}",
483 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800484 return;
485 }
486
487 std::scoped_lock lock(spiAutoMutex);
488 // FPGA only has one auto SPI engine
489 if (spiAutoPort != kSpiMaxHandles) {
490 *status = RESOURCE_IS_ALLOCATED;
491 return;
492 }
493
494 // remember the initialized port for other entry points
495 spiAutoPort = port;
496
497 // configure the correct chip select
498 if (port < 4) {
499 spiSystem->writeAutoSPI1Select(false, status);
500 spiSystem->writeAutoChipSelect(port, status);
501 } else {
502 spiSystem->writeAutoSPI1Select(true, status);
503 spiSystem->writeAutoChipSelect(0, status);
504 }
505
506 // configure DMA
Austin Schuh75263e32022-02-22 18:05:32 -0800507 spiAutoDMA =
508 std::make_unique<tDMAManager>(g_SpiAutoData_index, bufferSize, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800509}
510
511void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) {
512 if (port < 0 || port >= kSpiMaxHandles) {
513 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700514 hal::SetLastError(
515 status,
516 fmt::format("Serial port must be between 0 and {}. Requested {}",
517 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800518 return;
519 }
520
521 std::scoped_lock lock(spiAutoMutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700522 if (spiAutoPort != port) {
523 return;
524 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800525 spiAutoPort = kSpiMaxHandles;
526
527 // disable by setting to internal clock and setting rate=0
528 spiSystem->writeAutoRate(0, status);
529 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
530
531 // stop the DMA
532 spiAutoDMA->stop(status);
533
534 spiAutoDMA.reset(nullptr);
535
536 spiAutoRunning = false;
537}
538
539void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status) {
540 std::scoped_lock lock(spiAutoMutex);
541 // FPGA only has one auto SPI engine
542 if (port != spiAutoPort) {
543 *status = INCOMPATIBLE_STATE;
544 return;
545 }
546
547 spiAutoRunning = true;
548
549 // start the DMA
550 spiAutoDMA->start(status);
551
552 // auto rate is in microseconds
553 spiSystem->writeAutoRate(period * 1000000, status);
554
555 // disable the external clock
556 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
557}
558
559void HAL_StartSPIAutoTrigger(HAL_SPIPort port, HAL_Handle digitalSourceHandle,
560 HAL_AnalogTriggerType analogTriggerType,
561 HAL_Bool triggerRising, HAL_Bool triggerFalling,
562 int32_t* status) {
563 std::scoped_lock lock(spiAutoMutex);
564 // FPGA only has one auto SPI engine
565 if (port != spiAutoPort) {
566 *status = INCOMPATIBLE_STATE;
567 return;
568 }
569
570 spiAutoRunning = true;
571
572 // start the DMA
573 spiAutoDMA->start(status);
574
575 // get channel routing
576 bool routingAnalogTrigger = false;
577 uint8_t routingChannel = 0;
578 uint8_t routingModule = 0;
579 if (!remapDigitalSource(digitalSourceHandle, analogTriggerType,
580 routingChannel, routingModule,
581 routingAnalogTrigger)) {
582 *status = HAL_HANDLE_ERROR;
583 return;
584 }
585
586 // configure external trigger and enable it
587 tSPI::tAutoTriggerConfig config;
588 config.ExternalClock = 1;
589 config.FallingEdge = triggerFalling ? 1 : 0;
590 config.RisingEdge = triggerRising ? 1 : 0;
591 config.ExternalClockSource_AnalogTrigger = routingAnalogTrigger ? 1 : 0;
592 config.ExternalClockSource_Module = routingModule;
593 config.ExternalClockSource_Channel = routingChannel;
594 spiSystem->writeAutoTriggerConfig(config, status);
595}
596
597void HAL_StopSPIAuto(HAL_SPIPort port, int32_t* status) {
598 std::scoped_lock lock(spiAutoMutex);
599 // FPGA only has one auto SPI engine
600 if (port != spiAutoPort) {
601 *status = INCOMPATIBLE_STATE;
602 return;
603 }
604
605 // disable by setting to internal clock and setting rate=0
606 spiSystem->writeAutoRate(0, status);
607 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
608
609 // stop the DMA
610 spiAutoDMA->stop(status);
611
612 spiAutoRunning = false;
613}
614
615void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
616 int32_t dataSize, int32_t zeroSize,
617 int32_t* status) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800618 if (dataSize < 0 || dataSize > 32) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800619 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700620 hal::SetLastError(
621 status,
622 fmt::format(
623 "Data size must be between 0 and 32 inclusive. Requested {}",
624 dataSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800625 return;
626 }
627
Austin Schuh1e69f942020-11-14 15:06:14 -0800628 if (zeroSize < 0 || zeroSize > 127) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800629 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700630 hal::SetLastError(
631 status,
632 fmt::format(
633 "Zero size must be between 0 and 127 inclusive. Requested {}",
634 zeroSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800635 return;
636 }
637
638 std::scoped_lock lock(spiAutoMutex);
639 // FPGA only has one auto SPI engine
640 if (port != spiAutoPort) {
641 *status = INCOMPATIBLE_STATE;
642 return;
643 }
644
645 // set tx data registers
646 for (int32_t i = 0; i < dataSize; ++i)
647 spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
648
649 // set byte counts
650 tSPI::tAutoByteCount config;
651 config.ZeroByteCount = static_cast<unsigned>(zeroSize) & 0x7f;
652 config.TxByteCount = static_cast<unsigned>(dataSize) & 0x1f;
653 spiSystem->writeAutoByteCount(config, status);
654}
655
656void HAL_ForceSPIAutoRead(HAL_SPIPort port, int32_t* status) {
657 std::scoped_lock lock(spiAutoMutex);
658 // FPGA only has one auto SPI engine
659 if (port != spiAutoPort) {
660 *status = INCOMPATIBLE_STATE;
661 return;
662 }
663
664 spiSystem->strobeAutoForceOne(status);
665}
666
667int32_t HAL_ReadSPIAutoReceivedData(HAL_SPIPort port, uint32_t* buffer,
668 int32_t numToRead, double timeout,
669 int32_t* status) {
670 std::scoped_lock lock(spiAutoMutex);
671 // FPGA only has one auto SPI engine
672 if (port != spiAutoPort) {
673 *status = INCOMPATIBLE_STATE;
674 return 0;
675 }
676
677 size_t numRemaining = 0;
678 // timeout is in ms
679 spiAutoDMA->read(buffer, numToRead, timeout * 1000, &numRemaining, status);
680 return numRemaining;
681}
682
683int32_t HAL_GetSPIAutoDroppedCount(HAL_SPIPort port, int32_t* status) {
684 std::scoped_lock lock(spiAutoMutex);
685 // FPGA only has one auto SPI engine
686 if (port != spiAutoPort) {
687 *status = INCOMPATIBLE_STATE;
688 return 0;
689 }
690
691 return spiSystem->readTransferSkippedFullCount(status);
692}
693
694void HAL_ConfigureSPIAutoStall(HAL_SPIPort port, int32_t csToSclkTicks,
695 int32_t stallTicks, int32_t pow2BytesPerRead,
696 int32_t* status) {
697 std::scoped_lock lock(spiAutoMutex);
698 // FPGA only has one auto SPI engine
699 if (port != spiAutoPort) {
700 *status = INCOMPATIBLE_STATE;
701 return;
702 }
703
704 tSPI::tStallConfig stallConfig;
705 stallConfig.CsToSclkTicks = static_cast<uint8_t>(csToSclkTicks);
706 stallConfig.StallTicks = static_cast<uint16_t>(stallTicks);
707 stallConfig.Pow2BytesPerRead = static_cast<uint8_t>(pow2BytesPerRead);
708 spiSystem->writeStallConfig(stallConfig, status);
709}
710
711} // extern "C"