blob: dc4631e197b7781d27619609ad21e44405ab4d27 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "hal/DIO.h"
9
10#include <cmath>
11#include <thread>
12
13#include <wpi/raw_ostream.h>
14
15#include "DigitalInternal.h"
16#include "HALInitializer.h"
17#include "PortsInternal.h"
18#include "hal/cpp/fpga_clock.h"
19#include "hal/handles/HandlesInternal.h"
20#include "hal/handles/LimitedHandleResource.h"
21
22using namespace hal;
23
24// Create a mutex to protect changes to the DO PWM config
25static wpi::mutex digitalPwmMutex;
26
27static LimitedHandleResource<HAL_DigitalPWMHandle, uint8_t,
28 kNumDigitalPWMOutputs, HAL_HandleEnum::DigitalPWM>*
29 digitalPWMHandles;
30
31namespace hal {
32namespace init {
33void InitializeDIO() {
34 static LimitedHandleResource<HAL_DigitalPWMHandle, uint8_t,
35 kNumDigitalPWMOutputs,
36 HAL_HandleEnum::DigitalPWM>
37 dpH;
38 digitalPWMHandles = &dpH;
39}
40} // namespace init
41} // namespace hal
42
43extern "C" {
44
45HAL_DigitalHandle HAL_InitializeDIOPort(HAL_PortHandle portHandle,
46 HAL_Bool input, int32_t* status) {
47 hal::init::CheckInit();
48 initializeDigital(status);
49
50 if (*status != 0) return HAL_kInvalidHandle;
51
52 int16_t channel = getPortHandleChannel(portHandle);
53 if (channel == InvalidHandleIndex || channel >= kNumDigitalChannels) {
54 *status = PARAMETER_OUT_OF_RANGE;
55 return HAL_kInvalidHandle;
56 }
57
58 auto handle =
59 digitalChannelHandles->Allocate(channel, HAL_HandleEnum::DIO, status);
60
61 if (*status != 0)
62 return HAL_kInvalidHandle; // failed to allocate. Pass error back.
63
64 auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::DIO);
65 if (port == nullptr) { // would only occur on thread issue.
66 *status = HAL_HANDLE_ERROR;
67 return HAL_kInvalidHandle;
68 }
69
70 port->channel = static_cast<uint8_t>(channel);
71
72 std::scoped_lock lock(digitalDIOMutex);
73
74 tDIO::tOutputEnable outputEnable = digitalSystem->readOutputEnable(status);
75
76 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
77 if (!getPortHandleSPIEnable(portHandle)) {
78 // if this flag is not set, we actually want DIO.
79 uint32_t bitToSet = 1u << remapSPIChannel(port->channel);
80
81 uint16_t specialFunctions = spiSystem->readEnableDIO(status);
82 // Set the field to enable SPI DIO
83 spiSystem->writeEnableDIO(specialFunctions | bitToSet, status);
84
85 if (input) {
86 outputEnable.SPIPort =
87 outputEnable.SPIPort & (~bitToSet); // clear the field for read
88 } else {
89 outputEnable.SPIPort =
90 outputEnable.SPIPort | bitToSet; // set the bits for write
91 }
92 }
93 } else if (port->channel < kNumDigitalHeaders) {
94 uint32_t bitToSet = 1u << port->channel;
95 if (input) {
96 outputEnable.Headers =
97 outputEnable.Headers & (~bitToSet); // clear the bit for read
98 } else {
99 outputEnable.Headers =
100 outputEnable.Headers | bitToSet; // set the bit for write
101 }
102 } else {
103 uint32_t bitToSet = 1u << remapMXPChannel(port->channel);
104
105 uint16_t specialFunctions =
106 digitalSystem->readEnableMXPSpecialFunction(status);
107 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions & ~bitToSet,
108 status);
109
110 if (input) {
111 outputEnable.MXP =
112 outputEnable.MXP & (~bitToSet); // clear the bit for read
113 } else {
114 outputEnable.MXP = outputEnable.MXP | bitToSet; // set the bit for write
115 }
116 }
117
118 digitalSystem->writeOutputEnable(outputEnable, status);
119
120 return handle;
121}
122
123HAL_Bool HAL_CheckDIOChannel(int32_t channel) {
124 return channel < kNumDigitalChannels && channel >= 0;
125}
126
127void HAL_FreeDIOPort(HAL_DigitalHandle dioPortHandle) {
128 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
129 // no status, so no need to check for a proper free.
130 if (port == nullptr) return;
131 digitalChannelHandles->Free(dioPortHandle, HAL_HandleEnum::DIO);
132
133 // Wait for no other object to hold this handle.
134 auto start = hal::fpga_clock::now();
135 while (port.use_count() != 1) {
136 auto current = hal::fpga_clock::now();
137 if (start + std::chrono::seconds(1) < current) {
138 wpi::outs() << "DIO handle free timeout\n";
139 wpi::outs().flush();
140 break;
141 }
142 std::this_thread::yield();
143 }
144
145 int32_t status = 0;
146 std::scoped_lock lock(digitalDIOMutex);
147 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
148 // Unset the SPI flag
149 int32_t bitToUnset = 1 << remapSPIChannel(port->channel);
150 uint16_t specialFunctions = spiSystem->readEnableDIO(&status);
151 spiSystem->writeEnableDIO(specialFunctions & ~bitToUnset, &status);
152 } else if (port->channel >= kNumDigitalHeaders) {
153 // Unset the MXP flag
154 uint32_t bitToUnset = 1u << remapMXPChannel(port->channel);
155
156 uint16_t specialFunctions =
157 digitalSystem->readEnableMXPSpecialFunction(&status);
158 digitalSystem->writeEnableMXPSpecialFunction(specialFunctions | bitToUnset,
159 &status);
160 }
161}
162
163void HAL_SetDIOSimDevice(HAL_DigitalHandle handle, HAL_SimDeviceHandle device) {
164}
165
166HAL_DigitalPWMHandle HAL_AllocateDigitalPWM(int32_t* status) {
167 auto handle = digitalPWMHandles->Allocate();
168 if (handle == HAL_kInvalidHandle) {
169 *status = NO_AVAILABLE_RESOURCES;
170 return HAL_kInvalidHandle;
171 }
172
173 auto id = digitalPWMHandles->Get(handle);
174 if (id == nullptr) { // would only occur on thread issue.
175 *status = HAL_HANDLE_ERROR;
176 return HAL_kInvalidHandle;
177 }
178 *id = static_cast<uint8_t>(getHandleIndex(handle));
179
180 return handle;
181}
182
183void HAL_FreeDigitalPWM(HAL_DigitalPWMHandle pwmGenerator, int32_t* status) {
184 digitalPWMHandles->Free(pwmGenerator);
185}
186
187void HAL_SetDigitalPWMRate(double rate, int32_t* status) {
188 // Currently rounding in the log rate domain... heavy weight toward picking a
189 // higher freq.
190 // TODO: Round in the linear rate domain.
191 initializeDigital(status);
192 if (*status != 0) return;
193 uint16_t pwmPeriodPower = static_cast<uint16_t>(
194 std::log(1.0 / (16 * 1.0E-6 * rate)) / std::log(2.0) + 0.5);
195 digitalSystem->writePWMPeriodPower(pwmPeriodPower, status);
196}
197
198void HAL_SetDigitalPWMDutyCycle(HAL_DigitalPWMHandle pwmGenerator,
199 double dutyCycle, int32_t* status) {
200 auto port = digitalPWMHandles->Get(pwmGenerator);
201 if (port == nullptr) {
202 *status = HAL_HANDLE_ERROR;
203 return;
204 }
205 int32_t id = *port;
206 if (dutyCycle > 1.0) dutyCycle = 1.0;
207 if (dutyCycle < 0.0) dutyCycle = 0.0;
208 double rawDutyCycle = 256.0 * dutyCycle;
209 if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
210 {
211 std::scoped_lock lock(digitalPwmMutex);
212 uint16_t pwmPeriodPower = digitalSystem->readPWMPeriodPower(status);
213 if (pwmPeriodPower < 4) {
214 // The resolution of the duty cycle drops close to the highest
215 // frequencies.
216 rawDutyCycle = rawDutyCycle / std::pow(2.0, 4 - pwmPeriodPower);
217 }
218 if (id < 4)
219 digitalSystem->writePWMDutyCycleA(id, static_cast<uint8_t>(rawDutyCycle),
220 status);
221 else
222 digitalSystem->writePWMDutyCycleB(
223 id - 4, static_cast<uint8_t>(rawDutyCycle), status);
224 }
225}
226
227void HAL_SetDigitalPWMOutputChannel(HAL_DigitalPWMHandle pwmGenerator,
228 int32_t channel, int32_t* status) {
229 auto port = digitalPWMHandles->Get(pwmGenerator);
230 if (port == nullptr) {
231 *status = HAL_HANDLE_ERROR;
232 return;
233 }
234 int32_t id = *port;
235 if (channel >= kNumDigitalHeaders &&
236 channel <
237 kNumDigitalHeaders + kNumDigitalMXPChannels) { // If it is on the MXP
238 /* Then to write as a digital PWM channel an offset is needed to write on
239 * the correct channel
240 */
241 channel += kMXPDigitalPWMOffset;
242 }
243 digitalSystem->writePWMOutputSelect(id, channel, status);
244}
245
246void HAL_SetDIO(HAL_DigitalHandle dioPortHandle, HAL_Bool value,
247 int32_t* status) {
248 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
249 if (port == nullptr) {
250 *status = HAL_HANDLE_ERROR;
251 return;
252 }
253 if (value != 0 && value != 1) {
254 if (value != 0) value = 1;
255 }
256 {
257 std::scoped_lock lock(digitalDIOMutex);
258 tDIO::tDO currentDIO = digitalSystem->readDO(status);
259
260 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
261 if (value == 0) {
262 currentDIO.SPIPort =
263 currentDIO.SPIPort & ~(1u << remapSPIChannel(port->channel));
264 } else if (value == 1) {
265 currentDIO.SPIPort =
266 currentDIO.SPIPort | (1u << remapSPIChannel(port->channel));
267 }
268 } else if (port->channel < kNumDigitalHeaders) {
269 if (value == 0) {
270 currentDIO.Headers = currentDIO.Headers & ~(1u << port->channel);
271 } else if (value == 1) {
272 currentDIO.Headers = currentDIO.Headers | (1u << port->channel);
273 }
274 } else {
275 if (value == 0) {
276 currentDIO.MXP =
277 currentDIO.MXP & ~(1u << remapMXPChannel(port->channel));
278 } else if (value == 1) {
279 currentDIO.MXP =
280 currentDIO.MXP | (1u << remapMXPChannel(port->channel));
281 }
282 }
283 digitalSystem->writeDO(currentDIO, status);
284 }
285}
286
287void HAL_SetDIODirection(HAL_DigitalHandle dioPortHandle, HAL_Bool input,
288 int32_t* status) {
289 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
290 if (port == nullptr) {
291 *status = HAL_HANDLE_ERROR;
292 return;
293 }
294 {
295 std::scoped_lock lock(digitalDIOMutex);
296 tDIO::tOutputEnable currentDIO = digitalSystem->readOutputEnable(status);
297
298 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
299 if (input) {
300 currentDIO.SPIPort =
301 currentDIO.SPIPort & ~(1u << remapSPIChannel(port->channel));
302 } else {
303 currentDIO.SPIPort =
304 currentDIO.SPIPort | (1u << remapSPIChannel(port->channel));
305 }
306 } else if (port->channel < kNumDigitalHeaders) {
307 if (input) {
308 currentDIO.Headers = currentDIO.Headers & ~(1u << port->channel);
309 } else {
310 currentDIO.Headers = currentDIO.Headers | (1u << port->channel);
311 }
312 } else {
313 if (input) {
314 currentDIO.MXP =
315 currentDIO.MXP & ~(1u << remapMXPChannel(port->channel));
316 } else {
317 currentDIO.MXP =
318 currentDIO.MXP | (1u << remapMXPChannel(port->channel));
319 }
320 }
321 digitalSystem->writeOutputEnable(currentDIO, status);
322 }
323}
324
325HAL_Bool HAL_GetDIO(HAL_DigitalHandle dioPortHandle, int32_t* status) {
326 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
327 if (port == nullptr) {
328 *status = HAL_HANDLE_ERROR;
329 return false;
330 }
331 tDIO::tDI currentDIO = digitalSystem->readDI(status);
332 // Shift 00000001 over channel-1 places.
333 // AND it against the currentDIO
334 // if it == 0, then return false
335 // else return true
336
337 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
338 return ((currentDIO.SPIPort >> remapSPIChannel(port->channel)) & 1) != 0;
339 } else if (port->channel < kNumDigitalHeaders) {
340 return ((currentDIO.Headers >> port->channel) & 1) != 0;
341 } else {
342 return ((currentDIO.MXP >> remapMXPChannel(port->channel)) & 1) != 0;
343 }
344}
345
346HAL_Bool HAL_GetDIODirection(HAL_DigitalHandle dioPortHandle, int32_t* status) {
347 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
348 if (port == nullptr) {
349 *status = HAL_HANDLE_ERROR;
350 return false;
351 }
352 tDIO::tOutputEnable currentOutputEnable =
353 digitalSystem->readOutputEnable(status);
354 // Shift 00000001 over port->channel-1 places.
355 // AND it against the currentOutputEnable
356 // if it == 0, then return false
357 // else return true
358
359 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
360 return ((currentOutputEnable.SPIPort >> remapSPIChannel(port->channel)) &
361 1) != 0;
362 } else if (port->channel < kNumDigitalHeaders) {
363 return ((currentOutputEnable.Headers >> port->channel) & 1) != 0;
364 } else {
365 return ((currentOutputEnable.MXP >> remapMXPChannel(port->channel)) & 1) !=
366 0;
367 }
368}
369
370void HAL_Pulse(HAL_DigitalHandle dioPortHandle, double pulseLength,
371 int32_t* status) {
372 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
373 if (port == nullptr) {
374 *status = HAL_HANDLE_ERROR;
375 return;
376 }
377 tDIO::tPulse pulse;
378
379 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
380 pulse.SPIPort = 1u << remapSPIChannel(port->channel);
381 } else if (port->channel < kNumDigitalHeaders) {
382 pulse.Headers = 1u << port->channel;
383 } else {
384 pulse.MXP = 1u << remapMXPChannel(port->channel);
385 }
386
387 digitalSystem->writePulseLength(
388 static_cast<uint16_t>(1.0e9 * pulseLength /
389 (pwmSystem->readLoopTiming(status) * 25)),
390 status);
391 digitalSystem->writePulse(pulse, status);
392}
393
394HAL_Bool HAL_IsPulsing(HAL_DigitalHandle dioPortHandle, int32_t* status) {
395 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
396 if (port == nullptr) {
397 *status = HAL_HANDLE_ERROR;
398 return false;
399 }
400 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
401
402 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
403 return (pulseRegister.SPIPort & (1 << remapSPIChannel(port->channel))) != 0;
404 } else if (port->channel < kNumDigitalHeaders) {
405 return (pulseRegister.Headers & (1 << port->channel)) != 0;
406 } else {
407 return (pulseRegister.MXP & (1 << remapMXPChannel(port->channel))) != 0;
408 }
409}
410
411HAL_Bool HAL_IsAnyPulsing(int32_t* status) {
412 initializeDigital(status);
413 if (*status != 0) return false;
414 tDIO::tPulse pulseRegister = digitalSystem->readPulse(status);
415 return pulseRegister.Headers != 0 && pulseRegister.MXP != 0 &&
416 pulseRegister.SPIPort != 0;
417}
418
419void HAL_SetFilterSelect(HAL_DigitalHandle dioPortHandle, int32_t filterIndex,
420 int32_t* status) {
421 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
422 if (port == nullptr) {
423 *status = HAL_HANDLE_ERROR;
424 return;
425 }
426
427 std::scoped_lock lock(digitalDIOMutex);
428 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
429 // Channels 10-15 are SPI channels, so subtract our MXP channels
430 digitalSystem->writeFilterSelectHdr(port->channel - kNumDigitalMXPChannels,
431 filterIndex, status);
432 } else if (port->channel < kNumDigitalHeaders) {
433 digitalSystem->writeFilterSelectHdr(port->channel, filterIndex, status);
434 } else {
435 digitalSystem->writeFilterSelectMXP(remapMXPChannel(port->channel),
436 filterIndex, status);
437 }
438}
439
440int32_t HAL_GetFilterSelect(HAL_DigitalHandle dioPortHandle, int32_t* status) {
441 auto port = digitalChannelHandles->Get(dioPortHandle, HAL_HandleEnum::DIO);
442 if (port == nullptr) {
443 *status = HAL_HANDLE_ERROR;
444 return 0;
445 }
446
447 std::scoped_lock lock(digitalDIOMutex);
448 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
449 // Channels 10-15 are SPI channels, so subtract our MXP channels
450 return digitalSystem->readFilterSelectHdr(
451 port->channel - kNumDigitalMXPChannels, status);
452 } else if (port->channel < kNumDigitalHeaders) {
453 return digitalSystem->readFilterSelectHdr(port->channel, status);
454 } else {
455 return digitalSystem->readFilterSelectMXP(remapMXPChannel(port->channel),
456 status);
457 }
458}
459
460void HAL_SetFilterPeriod(int32_t filterIndex, int64_t value, int32_t* status) {
461 initializeDigital(status);
462 if (*status != 0) return;
463 std::scoped_lock lock(digitalDIOMutex);
464 digitalSystem->writeFilterPeriodHdr(filterIndex, value, status);
465 if (*status == 0) {
466 digitalSystem->writeFilterPeriodMXP(filterIndex, value, status);
467 }
468}
469
470int64_t HAL_GetFilterPeriod(int32_t filterIndex, int32_t* status) {
471 initializeDigital(status);
472 if (*status != 0) return 0;
473 uint32_t hdrPeriod = 0;
474 uint32_t mxpPeriod = 0;
475 {
476 std::scoped_lock lock(digitalDIOMutex);
477 hdrPeriod = digitalSystem->readFilterPeriodHdr(filterIndex, status);
478 if (*status == 0) {
479 mxpPeriod = digitalSystem->readFilterPeriodMXP(filterIndex, status);
480 }
481 }
482 if (hdrPeriod != mxpPeriod) {
483 *status = NiFpga_Status_SoftwareFault;
484 return -1;
485 }
486 return hdrPeriod;
487}
488
489} // extern "C"