blob: 0df6fdb824b30f8af408b27d700a802a5e1f3e01 [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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700128 fmt::print("Failed to open SPI port {}: {}\n", port,
129 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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700150 fmt::print("Failed to open SPI port {}: {}\n", port,
151 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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700173 fmt::print("Failed to open SPI port {}: {}\n", port,
174 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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700196 fmt::print("Failed to open SPI port {}: {}\n", port,
197 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) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700243 fmt::print("Failed to open SPI port {}: {}\n", port,
244 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
367void HAL_SetSPIOpts(HAL_SPIPort port, HAL_Bool msbFirst,
368 HAL_Bool sampleOnTrailing, HAL_Bool clkIdleHigh) {
369 if (port < 0 || port >= kSpiMaxHandles) {
370 return;
371 }
372
373 uint8_t mode = 0;
374 mode |= (!msbFirst ? 8 : 0);
375 mode |= (clkIdleHigh ? 2 : 0);
376 mode |= (sampleOnTrailing ? 1 : 0);
377
378 std::scoped_lock lock(spiApiMutexes[port]);
379 ioctl(HAL_GetSPIHandle(port), SPI_IOC_WR_MODE, &mode);
380}
381
382void HAL_SetSPIChipSelectActiveHigh(HAL_SPIPort port, int32_t* status) {
383 if (port < 0 || port >= kSpiMaxHandles) {
384 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700385 hal::SetLastError(
386 status,
387 fmt::format("Serial port must be between 0 and {}. Requested {}",
388 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800389 return;
390 }
391
392 std::scoped_lock lock(spiApiMutexes[port]);
393 if (port < 4) {
394 spiSystem->writeChipSelectActiveHigh_Hdr(
395 spiSystem->readChipSelectActiveHigh_Hdr(status) | (1 << port), status);
396 } else {
397 spiSystem->writeChipSelectActiveHigh_MXP(1, status);
398 }
399}
400
401void HAL_SetSPIChipSelectActiveLow(HAL_SPIPort port, int32_t* status) {
402 if (port < 0 || port >= kSpiMaxHandles) {
403 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700404 hal::SetLastError(
405 status,
406 fmt::format("Serial port must be between 0 and {}. Requested {}",
407 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800408 return;
409 }
410
411 std::scoped_lock lock(spiApiMutexes[port]);
412 if (port < 4) {
413 spiSystem->writeChipSelectActiveHigh_Hdr(
414 spiSystem->readChipSelectActiveHigh_Hdr(status) & ~(1 << port), status);
415 } else {
416 spiSystem->writeChipSelectActiveHigh_MXP(0, status);
417 }
418}
419
420int32_t HAL_GetSPIHandle(HAL_SPIPort port) {
421 if (port < 0 || port >= kSpiMaxHandles) {
422 return 0;
423 }
424
425 std::scoped_lock lock(spiHandleMutexes[port]);
426 switch (port) {
427 case 0:
428 return m_spiCS0Handle;
429 case 1:
430 return m_spiCS1Handle;
431 case 2:
432 return m_spiCS2Handle;
433 case 3:
434 return m_spiCS3Handle;
435 case 4:
436 return m_spiMXPHandle;
437 default:
438 return 0;
439 }
440}
441
442void HAL_SetSPIHandle(HAL_SPIPort port, int32_t handle) {
443 if (port < 0 || port >= kSpiMaxHandles) {
444 return;
445 }
446
447 std::scoped_lock lock(spiHandleMutexes[port]);
448 switch (port) {
449 case 0:
450 m_spiCS0Handle = handle;
451 break;
452 case 1:
453 m_spiCS1Handle = handle;
454 break;
455 case 2:
456 m_spiCS2Handle = handle;
457 break;
458 case 3:
459 m_spiCS3Handle = handle;
460 break;
461 case 4:
462 m_spiMXPHandle = handle;
463 break;
464 default:
465 break;
466 }
467}
468
469void HAL_InitSPIAuto(HAL_SPIPort port, int32_t bufferSize, int32_t* status) {
470 if (port < 0 || port >= kSpiMaxHandles) {
471 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700472 hal::SetLastError(
473 status,
474 fmt::format("Serial port must be between 0 and {}. Requested {}",
475 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800476 return;
477 }
478
479 std::scoped_lock lock(spiAutoMutex);
480 // FPGA only has one auto SPI engine
481 if (spiAutoPort != kSpiMaxHandles) {
482 *status = RESOURCE_IS_ALLOCATED;
483 return;
484 }
485
486 // remember the initialized port for other entry points
487 spiAutoPort = port;
488
489 // configure the correct chip select
490 if (port < 4) {
491 spiSystem->writeAutoSPI1Select(false, status);
492 spiSystem->writeAutoChipSelect(port, status);
493 } else {
494 spiSystem->writeAutoSPI1Select(true, status);
495 spiSystem->writeAutoChipSelect(0, status);
496 }
497
498 // configure DMA
Austin Schuh75263e32022-02-22 18:05:32 -0800499 spiAutoDMA =
500 std::make_unique<tDMAManager>(g_SpiAutoData_index, bufferSize, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800501}
502
503void HAL_FreeSPIAuto(HAL_SPIPort port, int32_t* status) {
504 if (port < 0 || port >= kSpiMaxHandles) {
505 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700506 hal::SetLastError(
507 status,
508 fmt::format("Serial port must be between 0 and {}. Requested {}",
509 kSpiMaxHandles, static_cast<int>(port)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800510 return;
511 }
512
513 std::scoped_lock lock(spiAutoMutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700514 if (spiAutoPort != port) {
515 return;
516 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800517 spiAutoPort = kSpiMaxHandles;
518
519 // disable by setting to internal clock and setting rate=0
520 spiSystem->writeAutoRate(0, status);
521 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
522
523 // stop the DMA
524 spiAutoDMA->stop(status);
525
526 spiAutoDMA.reset(nullptr);
527
528 spiAutoRunning = false;
529}
530
531void HAL_StartSPIAutoRate(HAL_SPIPort port, double period, int32_t* status) {
532 std::scoped_lock lock(spiAutoMutex);
533 // FPGA only has one auto SPI engine
534 if (port != spiAutoPort) {
535 *status = INCOMPATIBLE_STATE;
536 return;
537 }
538
539 spiAutoRunning = true;
540
541 // start the DMA
542 spiAutoDMA->start(status);
543
544 // auto rate is in microseconds
545 spiSystem->writeAutoRate(period * 1000000, status);
546
547 // disable the external clock
548 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
549}
550
551void HAL_StartSPIAutoTrigger(HAL_SPIPort port, HAL_Handle digitalSourceHandle,
552 HAL_AnalogTriggerType analogTriggerType,
553 HAL_Bool triggerRising, HAL_Bool triggerFalling,
554 int32_t* status) {
555 std::scoped_lock lock(spiAutoMutex);
556 // FPGA only has one auto SPI engine
557 if (port != spiAutoPort) {
558 *status = INCOMPATIBLE_STATE;
559 return;
560 }
561
562 spiAutoRunning = true;
563
564 // start the DMA
565 spiAutoDMA->start(status);
566
567 // get channel routing
568 bool routingAnalogTrigger = false;
569 uint8_t routingChannel = 0;
570 uint8_t routingModule = 0;
571 if (!remapDigitalSource(digitalSourceHandle, analogTriggerType,
572 routingChannel, routingModule,
573 routingAnalogTrigger)) {
574 *status = HAL_HANDLE_ERROR;
575 return;
576 }
577
578 // configure external trigger and enable it
579 tSPI::tAutoTriggerConfig config;
580 config.ExternalClock = 1;
581 config.FallingEdge = triggerFalling ? 1 : 0;
582 config.RisingEdge = triggerRising ? 1 : 0;
583 config.ExternalClockSource_AnalogTrigger = routingAnalogTrigger ? 1 : 0;
584 config.ExternalClockSource_Module = routingModule;
585 config.ExternalClockSource_Channel = routingChannel;
586 spiSystem->writeAutoTriggerConfig(config, status);
587}
588
589void HAL_StopSPIAuto(HAL_SPIPort port, int32_t* status) {
590 std::scoped_lock lock(spiAutoMutex);
591 // FPGA only has one auto SPI engine
592 if (port != spiAutoPort) {
593 *status = INCOMPATIBLE_STATE;
594 return;
595 }
596
597 // disable by setting to internal clock and setting rate=0
598 spiSystem->writeAutoRate(0, status);
599 spiSystem->writeAutoTriggerConfig_ExternalClock(false, status);
600
601 // stop the DMA
602 spiAutoDMA->stop(status);
603
604 spiAutoRunning = false;
605}
606
607void HAL_SetSPIAutoTransmitData(HAL_SPIPort port, const uint8_t* dataToSend,
608 int32_t dataSize, int32_t zeroSize,
609 int32_t* status) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800610 if (dataSize < 0 || dataSize > 32) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800611 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700612 hal::SetLastError(
613 status,
614 fmt::format(
615 "Data size must be between 0 and 32 inclusive. Requested {}",
616 dataSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800617 return;
618 }
619
Austin Schuh1e69f942020-11-14 15:06:14 -0800620 if (zeroSize < 0 || zeroSize > 127) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800621 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700622 hal::SetLastError(
623 status,
624 fmt::format(
625 "Zero size must be between 0 and 127 inclusive. Requested {}",
626 zeroSize));
Brian Silverman8fce7482020-01-05 13:18:21 -0800627 return;
628 }
629
630 std::scoped_lock lock(spiAutoMutex);
631 // FPGA only has one auto SPI engine
632 if (port != spiAutoPort) {
633 *status = INCOMPATIBLE_STATE;
634 return;
635 }
636
637 // set tx data registers
638 for (int32_t i = 0; i < dataSize; ++i)
639 spiSystem->writeAutoTx(i >> 2, i & 3, dataToSend[i], status);
640
641 // set byte counts
642 tSPI::tAutoByteCount config;
643 config.ZeroByteCount = static_cast<unsigned>(zeroSize) & 0x7f;
644 config.TxByteCount = static_cast<unsigned>(dataSize) & 0x1f;
645 spiSystem->writeAutoByteCount(config, status);
646}
647
648void HAL_ForceSPIAutoRead(HAL_SPIPort port, int32_t* status) {
649 std::scoped_lock lock(spiAutoMutex);
650 // FPGA only has one auto SPI engine
651 if (port != spiAutoPort) {
652 *status = INCOMPATIBLE_STATE;
653 return;
654 }
655
656 spiSystem->strobeAutoForceOne(status);
657}
658
659int32_t HAL_ReadSPIAutoReceivedData(HAL_SPIPort port, uint32_t* buffer,
660 int32_t numToRead, double timeout,
661 int32_t* status) {
662 std::scoped_lock lock(spiAutoMutex);
663 // FPGA only has one auto SPI engine
664 if (port != spiAutoPort) {
665 *status = INCOMPATIBLE_STATE;
666 return 0;
667 }
668
669 size_t numRemaining = 0;
670 // timeout is in ms
671 spiAutoDMA->read(buffer, numToRead, timeout * 1000, &numRemaining, status);
672 return numRemaining;
673}
674
675int32_t HAL_GetSPIAutoDroppedCount(HAL_SPIPort port, int32_t* status) {
676 std::scoped_lock lock(spiAutoMutex);
677 // FPGA only has one auto SPI engine
678 if (port != spiAutoPort) {
679 *status = INCOMPATIBLE_STATE;
680 return 0;
681 }
682
683 return spiSystem->readTransferSkippedFullCount(status);
684}
685
686void HAL_ConfigureSPIAutoStall(HAL_SPIPort port, int32_t csToSclkTicks,
687 int32_t stallTicks, int32_t pow2BytesPerRead,
688 int32_t* status) {
689 std::scoped_lock lock(spiAutoMutex);
690 // FPGA only has one auto SPI engine
691 if (port != spiAutoPort) {
692 *status = INCOMPATIBLE_STATE;
693 return;
694 }
695
696 tSPI::tStallConfig stallConfig;
697 stallConfig.CsToSclkTicks = static_cast<uint8_t>(csToSclkTicks);
698 stallConfig.StallTicks = static_cast<uint16_t>(stallTicks);
699 stallConfig.Pow2BytesPerRead = static_cast<uint8_t>(pow2BytesPerRead);
700 spiSystem->writeStallConfig(stallConfig, status);
701}
702
703} // extern "C"