Squashed 'third_party/allwpilib_2019/' changes from 936627bd9..a3f7420da

e20d96ea4 Use __has_include for WPILib.h (#2164)
a76d006a0 Update native-utils to 2020.7.2 (#2161)
24c031d69 Increase SPI auto byte count to allow 32 bytes to be sent (#2163)
6b4eecf5f Add hidden functions to get the SPI system and SPI DMA (#2162)
ccdd0fbdb Add TrapezoidProfile external PID examples (#2131)
5c6b8a0f4 Replace std::is_pod_v with std::is_standard_layout_v (#2159)
67d2fed68 Add DutyCycleEncoder channel constructor (#2158)
d8f11eb14 Hardcode channels for LSB weight (#2153)
b2ae75acd Add way to disable "no extensions found" message (#2134)
4f951789f Build testbench tests online inorder to improve speed (#2144)
005c4c5be Try catch around task dependencies to fix loading in editor (#2156)
34f6b3f4c Fix C++ RamseteCommand param doxygen (#2157)
f7a93713f Fix up templated TrapezoidProfile classes (#2151)
8c2ff94d7 Rename MathUtils to MathUtil for consistency with other util classes (#2155)
d003ec2dc Update to 2020v9 image (#2154)
8e7cc3fe7 Add user-friendly SimDeviceSim wrappers (#2150)
6c8f6cf47 Fix bug in cubic and quintic hermetic spline generation (#2139)
e37ecd33a Sim GUI: Add support for LED displays (#2138)
57c5523d6 Fix documentation in RamseteCommand (#2149)
7b9c6ebc2 Fix wpiutilJNIShared linkage typo in wpilibj (#2143)
9a515c80f Template C++ LinearFilter to work with unit types (#2142)
5b73c17f2 Remove encoder velocities methods in DifferentialDriveOdometry (#2147)
b8c102426 Fix PS3Eye VID and PID (#2146)
2622c6c29 Add default values for b and zeta in RamseteController (#2145)
f66ae5999 Add HSV helpers to AddressableLED (#2135)
5e97c81d8 Add MedianFilter class for moving-window median (#2136)
f79b7a058 Remove unnecessary constructor arg for LinearFilter's circular buffers (#2140)
e49494830 Replace Jenkins with Azure agent (#1914)
b67d049ac Check status of PDP CAN reads (#2126)
70102a60b SendableRegistry.foreachLiveWindow: Prevent concurrent modification (#2129)
6dcd2b0e2 Improve various subsystem APIs (#2130)
ce3973435 HAL_CAN_ReceiveMessage: return MessageNotFound if no callbacks registered (#2133)
3fcfc8ea7 Fix double disable segfaulting interrupts (#2132)
6ceafe3cd Fix class reference for logger (#2123)
b058dcf64 Catch exceptions generated by OpenCV in cscore JNI (#2118)
0b9307fdf Remove unused parts of .styleguide files (#2119)
39be812b2 Fix C++ ArmFeedforward (#2120)
21e957ee4 Add DifferentialDrive voltage constraint (#2075)
e0bc97f66 Add ProfiledPIDSubsystem example (#2076)
3df44c874 Remove Rotation2d.h wpi/math include (#2117)
a58dbec8a Add holonomic follower examples (#2052)
9a8067465 Fix incomplete .styleguide (#2113)
ffa4b907c Fix C++ floating point literal formatting (#2114)
3d1ca856b Fix missing typename and return type (#2115)
5f85457a9 Add usage reporting for AddressableLED (#2108)
4ebae1712 Enforce leading/trailing zeros in Java numeric constants (#2105)
fa85fbfc1 Template C++ TrapezoidProfile and ProfiledPIDController on units (#2109)
f62e23f1a Add Doxygen for new HAL interfaces (#2110)
5443fdabc Directly construct PWM port from HAL, avoid wpilib PWM object (#2106)
c0e36df9d Standardize on PWMVictorSPX in examples (#2104)
8c4d9f541 Add TrapezoidProfileSubsystem (#2077)
45201d15f Add encoder distance overload to DifferentialDriveOdometry (#2096)
845aba33f Make feedforward classes constexpr (#2103)
500c43fb8 Add examples for DMA, DutyCycle, DutyCycleEncoder and AddressableLED (#2100)
589162811 Use DifferentialDriveWheelSpeeds in RamseteCommand ctor (#2091)
b37b68daa Add JRE deployment to MyRobot Deploy (#2099)
0e83c65d2 Fix small logic error in ParallelDeadlineGroup (#2095)
6f6c6da9f Updates to addressable LED (#2098)
1894219ef Fix devmain package in commands (#2097)
77a9949bb Add AddressableLED simulation GUI support
a4c9e4ec2 Add AddressableLED simulation support
8ed205907 Add AddressableLED (#2092)
59507b12d Bump to 2020 v7 image (#2086)
5d39bf806 Make halsimgui::DrawLEDs() values parameter const (#2088)
841ef91c0 Use gyro angle instead of robot angle for odometry (#2081)
1b66ead49 Use standard constant for pi instead of 3.14 (#2084)
db2c3dddd Use DMA Global Number for DMA Index (#2085)
82b2170fe Add DMA support to HAL and WPILibC (#2080)
8280b7e3a Add DutyCycleEncoder and AnalogEncoder (#2040)
551096006 Use kNumSystems for DutyCycle count in Ports (#2083)
df1065218 Remove release configs of deploy in MyRobot (#2082)
bf5388393 Add deploy options to myRobot (#2079)
b7bc1ea74 Update to 2020v6 image (#2078)
708009cd2 Update to gradle 6.0 (#2074)
3cce61b89 Add SmartDashboard::GetEntry function in C++ (#2064)
565e1f3e7 Fix spelling in MecanumDriveOdometry docs (#2072)
1853f7b6b Add timing window to simulation GUI
c5a049712 Add simulation pause/resume/step support
f5446c740 Add Notifier HALSIM access
3e049e02f Add name to HAL Notifier
2da64d15f Make usage reporting enums type match (#2069)
f04d95e50 Make FRCUsageReporting.h C-compatible (#2070)
d748c67a5 Generate docs for command libraries and fix doclint enable (#2071)
55a7f2b4a Add template for old command-based style (#2031)
486fa9c69 Add XboxController examples for arcade and tank drive (#2058)
e3dd1c5d7 Fix small bug in SplineHelper (#2061)
7dc7c71b5 Add feedforward components (#2045)
5f33d6af1 Fix ProfiledPIDSubsystem parameter name (#2017)
94843adb8 Standardize documentation of Speed Controllers bounds (#2043)
9bcff37b9 Add HAL specific version of wpi_setError (#2055)
326aecc9a Add error message for CAN Write Overrun (#2062)
00228678d Add requirements param to more Command APIs (#2059)
ff39a96ce Make DigitalOutput a DigitalSource (#2054)
5ccad2e8a Fix frc2::Timer returning incorrect timestamp values (#2057)
629e95776 Add VendorDeps JSON files for command libraries (#2048)
6858a57f7 Make notifier command tests a lot more lenient (#2056)
0ebe32823 Fix MyRobotStatic accidentally linking to shared command libs (#2046)
384d00f9e Fix various duty cycle bugs (#2047)
1f6850adf Add CAN Manufacturer for Studica (#2050)
7508aada9 Add ability to end startCompetition() main loop (#2032)
f5b4be16d Old C++ Command: Make GetName et al public (#2042)
e6f5c93ab Clean up new C++ commands (#2027)
39f46ceab Don't allow rising and falling values to be read from AnalogTrigger (#2039)
d93aa2b6b Add missing lock in FRCDriverStation (#2034)
114ddaf81 Fix duplicate encoders in examples (#2033)
f22d0961e Sim GUI: Add duty cycle support
3262c2bad Sim GUI: Use new multi-channel PDP getter function
96d40192a Revert accidental change to MyRobot.cpp (#2029)
ed30d5d40 Add JSON support for Trajectories (#2025)
2b6811edd Fix null pointer dereference in C++ CommandScheduler (#2023)
1d695a166 Add FPGA Duty Cycle support (#1987)
509819d83 Split the two command implementations into separate libraries (#2012)
2ad15cae1 Add multi PDP getter and sim PCM/PDP multi arg functions (#2014)
931b8ceef Add new usage reporting types from 2020v5 (#2026)
0b3821eba Change files with CRLF line endings to LF (#2022)
6f159d142 Add way to atomically check for new data, and wait otherwise (#2015)
a769f1f22 Fix bug in RamseteCommand (using degrees instead of radians) (#2020)
c5186d815 Clean up PIDCommand (#2010)
9ebd23d61 Add setVoltage method to SpeedController (#1997)
f6e311ef8 Fix SplineHelper bug (#2018)
f33bd9f05 Fix NPE in RamseteCommand (#2019)
1c1e0c9a6 Add HAL_SetAllSolenoids to sim (#2004)
ea9bb651a Remove accidental OpenCV link from wpilibc shared library (#2013)
cc0742518 Change command decorators to return implementation (#2007)
16b34cce2 Remove IterativeRobot templates (#2011)
669127e49 Update intellisense to work with Beta 2020 code (#2008)
9dc30797e Fix usage reporting indices (#2009)
f6b844ea3 Move HAL Interrupt struct to anonymous namespace (#2003)
a72f80991 Add extern C to DigitalGlitchFilterJNI (#2002)
916596cb0 Fix invalid examples json, add validator (#2001)
5509a8e96 Use constexpr for all example constants
0be6b6475 Use constexpr for DifferentialDriveKinematics

Change-Id: I1416646cbab487ad8021830215766d8ec7f24ddc
git-subtree-dir: third_party/allwpilib_2019
git-subtree-split: a3f7420dab7a104c27a0c3bf0872c999c98fd9a9
diff --git a/hal/src/main/native/athena/DMA.cpp b/hal/src/main/native/athena/DMA.cpp
new file mode 100644
index 0000000..ee39d2d
--- /dev/null
+++ b/hal/src/main/native/athena/DMA.cpp
@@ -0,0 +1,1006 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in the root directory of */
+/* the project.                                                               */
+/*----------------------------------------------------------------------------*/
+
+#include "hal/DMA.h"
+
+#include <array>
+#include <cstddef>
+#include <cstring>
+#include <memory>
+#include <type_traits>
+
+#include "AnalogInternal.h"
+#include "DigitalInternal.h"
+#include "EncoderInternal.h"
+#include "PortsInternal.h"
+#include "hal/AnalogAccumulator.h"
+#include "hal/AnalogGyro.h"
+#include "hal/AnalogInput.h"
+#include "hal/ChipObject.h"
+#include "hal/Errors.h"
+#include "hal/HALBase.h"
+#include "hal/handles/HandlesInternal.h"
+#include "hal/handles/LimitedHandleResource.h"
+#include "hal/handles/UnlimitedHandleResource.h"
+
+using namespace hal;
+
+static_assert(std::is_standard_layout_v<HAL_DMASample>,
+              "HAL_DMASample must have standard layout");
+
+namespace {
+
+struct DMA {
+  std::unique_ptr<tDMAManager> manager;
+  std::unique_ptr<tDMA> aDMA;
+
+  HAL_DMASample captureStore;
+};
+}  // namespace
+
+static constexpr size_t kChannelSize[22] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3, 2,
+                                            1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4};
+
+enum DMAOffsetConstants {
+  kEnable_AI0_Low = 0,
+  kEnable_AI0_High = 1,
+  kEnable_AIAveraged0_Low = 2,
+  kEnable_AIAveraged0_High = 3,
+  kEnable_AI1_Low = 4,
+  kEnable_AI1_High = 5,
+  kEnable_AIAveraged1_Low = 6,
+  kEnable_AIAveraged1_High = 7,
+  kEnable_Accumulator0 = 8,
+  kEnable_Accumulator1 = 9,
+  kEnable_DI = 10,
+  kEnable_AnalogTriggers = 11,
+  kEnable_Counters_Low = 12,
+  kEnable_Counters_High = 13,
+  kEnable_CounterTimers_Low = 14,
+  kEnable_CounterTimers_High = 15,
+  kEnable_Encoders_Low = 16,
+  kEnable_Encoders_High = 17,
+  kEnable_EncoderTimers_Low = 18,
+  kEnable_EncoderTimers_High = 19,
+  kEnable_DutyCycle_Low = 20,
+  kEnable_DutyCycle_High = 21,
+};
+
+static hal::LimitedHandleResource<HAL_DMAHandle, DMA, 1, HAL_HandleEnum::DMA>*
+    dmaHandles;
+
+namespace hal {
+namespace init {
+void InitializeDMA() {
+  static hal::LimitedHandleResource<HAL_DMAHandle, DMA, 1, HAL_HandleEnum::DMA>
+      dH;
+  dmaHandles = &dH;
+}
+}  // namespace init
+}  // namespace hal
+
+extern "C" {
+
+HAL_DMAHandle HAL_InitializeDMA(int32_t* status) {
+  HAL_Handle handle = dmaHandles->Allocate();
+  if (handle == HAL_kInvalidHandle) {
+    *status = NO_AVAILABLE_RESOURCES;
+    return HAL_kInvalidHandle;
+  }
+
+  auto dma = dmaHandles->Get(handle);
+
+  if (!dma) {
+    // Can only happen on thread error
+    *status = HAL_HANDLE_ERROR;
+    return HAL_kInvalidHandle;
+  }
+
+  // Manager does not get created until DMA is started
+  dma->aDMA.reset(tDMA::create(status));
+  if (*status != 0) {
+    dmaHandles->Free(handle);
+    return HAL_kInvalidHandle;
+  }
+
+  dma->aDMA->writeConfig_ExternalClock(false, status);
+  if (*status != 0) {
+    dmaHandles->Free(handle);
+    return HAL_kInvalidHandle;
+  }
+
+  HAL_SetDMARate(handle, 1, status);
+  if (*status != 0) {
+    dmaHandles->Free(handle);
+    return HAL_kInvalidHandle;
+  }
+
+  HAL_SetDMAPause(handle, false, status);
+  return handle;
+}
+
+void HAL_FreeDMA(HAL_DMAHandle handle) {
+  auto dma = dmaHandles->Get(handle);
+  dmaHandles->Free(handle);
+
+  if (!dma) return;
+
+  int32_t status = 0;
+  if (dma->manager) {
+    dma->manager->stop(&status);
+  }
+}
+
+void HAL_SetDMAPause(HAL_DMAHandle handle, HAL_Bool pause, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  dma->aDMA->writeConfig_Pause(pause, status);
+}
+void HAL_SetDMARate(HAL_DMAHandle handle, int32_t cycles, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (cycles < 1) {
+    cycles = 1;
+  }
+
+  dma->aDMA->writeRate(static_cast<uint32_t>(cycles), status);
+}
+
+void HAL_AddDMAEncoder(HAL_DMAHandle handle, HAL_EncoderHandle encoderHandle,
+                       int32_t* status) {
+  // Detect a counter encoder vs an actual encoder, and use the right DMA calls
+  HAL_FPGAEncoderHandle fpgaEncoderHandle = HAL_kInvalidHandle;
+  HAL_CounterHandle counterHandle = HAL_kInvalidHandle;
+
+  bool validEncoderHandle = hal::GetEncoderBaseHandle(
+      encoderHandle, &fpgaEncoderHandle, &counterHandle);
+
+  if (!validEncoderHandle) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (counterHandle != HAL_kInvalidHandle) {
+    HAL_AddDMACounter(handle, counterHandle, status);
+    return;
+  }
+
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(fpgaEncoderHandle) != HAL_HandleEnum::FPGAEncoder) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(fpgaEncoderHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_Encoders_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_Encoders_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMAEncoderPeriod(HAL_DMAHandle handle,
+                             HAL_EncoderHandle encoderHandle, int32_t* status) {
+  // Detect a counter encoder vs an actual encoder, and use the right DMA calls
+  HAL_FPGAEncoderHandle fpgaEncoderHandle = HAL_kInvalidHandle;
+  HAL_CounterHandle counterHandle = HAL_kInvalidHandle;
+
+  bool validEncoderHandle = hal::GetEncoderBaseHandle(
+      encoderHandle, &fpgaEncoderHandle, &counterHandle);
+
+  if (!validEncoderHandle) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (counterHandle != HAL_kInvalidHandle) {
+    HAL_AddDMACounterPeriod(handle, counterHandle, status);
+    return;
+  }
+
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(fpgaEncoderHandle) != HAL_HandleEnum::FPGAEncoder) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(fpgaEncoderHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_EncoderTimers_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_EncoderTimers_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMACounter(HAL_DMAHandle handle, HAL_CounterHandle counterHandle,
+                       int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(counterHandle) != HAL_HandleEnum::Counter) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(counterHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_Counters_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_Counters_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMACounterPeriod(HAL_DMAHandle handle,
+                             HAL_CounterHandle counterHandle, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(counterHandle) != HAL_HandleEnum::Counter) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(counterHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_CounterTimers_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_CounterTimers_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMADigitalSource(HAL_DMAHandle handle,
+                             HAL_Handle digitalSourceHandle, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (isHandleType(digitalSourceHandle, HAL_HandleEnum::AnalogTrigger)) {
+    dma->aDMA->writeConfig_Enable_AnalogTriggers(true, status);
+  } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::DIO)) {
+    dma->aDMA->writeConfig_Enable_DI(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMAAnalogInput(HAL_DMAHandle handle,
+                           HAL_AnalogInputHandle aInHandle, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(aInHandle) != HAL_HandleEnum::AnalogInput) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_AI0_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_AI0_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMADutyCycle(HAL_DMAHandle handle,
+                         HAL_DutyCycleHandle dutyCycleHandle, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(dutyCycleHandle) != HAL_HandleEnum::DutyCycle) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(dutyCycleHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_DutyCycle_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_DutyCycle_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMAAveragedAnalogInput(HAL_DMAHandle handle,
+                                   HAL_AnalogInputHandle aInHandle,
+                                   int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (getHandleType(aInHandle) != HAL_HandleEnum::AnalogInput) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index < 4) {
+    dma->aDMA->writeConfig_Enable_AIAveraged0_Low(true, status);
+  } else if (index < 8) {
+    dma->aDMA->writeConfig_Enable_AIAveraged0_High(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_AddDMAAnalogAccumulator(HAL_DMAHandle handle,
+                                 HAL_AnalogInputHandle aInHandle,
+                                 int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  if (!HAL_IsAccumulatorChannel(aInHandle, status)) {
+    *status = HAL_INVALID_ACCUMULATOR_CHANNEL;
+    return;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (index == 0) {
+    dma->aDMA->writeConfig_Enable_Accumulator0(true, status);
+  } else if (index == 1) {
+    dma->aDMA->writeConfig_Enable_Accumulator1(true, status);
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+}
+
+void HAL_SetDMAExternalTrigger(HAL_DMAHandle handle,
+                               HAL_Handle digitalSourceHandle,
+                               HAL_AnalogTriggerType analogTriggerType,
+                               HAL_Bool rising, HAL_Bool falling,
+                               int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = HAL_INVALID_DMA_ADDITION;
+    return;
+  }
+
+  int index = 0;
+  auto triggerChannels = dma->captureStore.triggerChannels;
+  do {
+    if (((triggerChannels >> index) & 0x1) == 0) {
+      break;
+    }
+    index++;
+  } while (index < 8);
+
+  if (index == 8) {
+    *status = NO_AVAILABLE_RESOURCES;
+    return;
+  }
+
+  dma->captureStore.triggerChannels |= (1 << index);
+
+  auto channelIndex = index;
+
+  auto isExternalClock = dma->aDMA->readConfig_ExternalClock(status);
+  if (*status == 0 && !isExternalClock) {
+    dma->aDMA->writeConfig_ExternalClock(true, status);
+    if (*status != 0) return;
+  } else if (*status != 0) {
+    return;
+  }
+
+  uint8_t pin = 0;
+  uint8_t module = 0;
+  bool analogTrigger = false;
+  bool success = remapDigitalSource(digitalSourceHandle, analogTriggerType, pin,
+                                    module, analogTrigger);
+
+  if (!success) {
+    *status = PARAMETER_OUT_OF_RANGE;
+    return;
+  }
+
+  tDMA::tExternalTriggers newTrigger;
+  newTrigger.FallingEdge = falling;
+  newTrigger.RisingEdge = rising;
+  newTrigger.ExternalClockSource_AnalogTrigger = analogTrigger;
+  newTrigger.ExternalClockSource_Channel = pin;
+  newTrigger.ExternalClockSource_Module = module;
+
+  dma->aDMA->writeExternalTriggers(channelIndex / 4, channelIndex % 4,
+                                   newTrigger, status);
+}
+
+void HAL_StartDMA(HAL_DMAHandle handle, int32_t queueDepth, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    *status = INCOMPATIBLE_STATE;
+    return;
+  }
+
+  tDMA::tConfig config = dma->aDMA->readConfig(status);
+  if (*status != 0) return;
+
+  {
+    size_t accum_size = 0;
+#define SET_SIZE(bit)                                      \
+  if (config.bit) {                                        \
+    dma->captureStore.channelOffsets[k##bit] = accum_size; \
+    accum_size += kChannelSize[k##bit];                    \
+  } else {                                                 \
+    dma->captureStore.channelOffsets[k##bit] = -1;         \
+  }
+    SET_SIZE(Enable_AI0_Low);
+    SET_SIZE(Enable_AI0_High);
+    SET_SIZE(Enable_AIAveraged0_Low);
+    SET_SIZE(Enable_AIAveraged0_High);
+    SET_SIZE(Enable_AI1_Low);
+    SET_SIZE(Enable_AI1_High);
+    SET_SIZE(Enable_AIAveraged1_Low);
+    SET_SIZE(Enable_AIAveraged1_High);
+    SET_SIZE(Enable_Accumulator0);
+    SET_SIZE(Enable_Accumulator1);
+    SET_SIZE(Enable_DI);
+    SET_SIZE(Enable_AnalogTriggers);
+    SET_SIZE(Enable_Counters_Low);
+    SET_SIZE(Enable_Counters_High);
+    SET_SIZE(Enable_CounterTimers_Low);
+    SET_SIZE(Enable_CounterTimers_High);
+    SET_SIZE(Enable_Encoders_Low);
+    SET_SIZE(Enable_Encoders_High);
+    SET_SIZE(Enable_EncoderTimers_Low);
+    SET_SIZE(Enable_EncoderTimers_High);
+    SET_SIZE(Enable_DutyCycle_Low);
+    SET_SIZE(Enable_DutyCycle_High);
+#undef SET_SIZE
+    dma->captureStore.captureSize = accum_size + 1;
+  }
+
+  dma->manager = std::make_unique<tDMAManager>(
+      g_DMA_index, queueDepth * dma->captureStore.captureSize, status);
+  if (*status != 0) {
+    return;
+  }
+
+  dma->manager->start(status);
+  dma->manager->stop(status);
+  dma->manager->start(status);
+}
+
+void HAL_StopDMA(HAL_DMAHandle handle, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  if (dma->manager) {
+    dma->manager->stop(status);
+    dma->manager = nullptr;
+  }
+}
+
+void* HAL_GetDMADirectPointer(HAL_DMAHandle handle) {
+  auto dma = dmaHandles->Get(handle);
+  return dma.get();
+}
+
+enum HAL_DMAReadStatus HAL_ReadDMADirect(void* dmaPointer,
+                                         HAL_DMASample* dmaSample,
+                                         int32_t timeoutMs,
+                                         int32_t* remainingOut,
+                                         int32_t* status) {
+  DMA* dma = static_cast<DMA*>(dmaPointer);
+  *remainingOut = 0;
+  size_t remainingBytes = 0;
+
+  if (!dma->manager) {
+    *status = INCOMPATIBLE_STATE;
+    return HAL_DMA_ERROR;
+  }
+
+  dma->manager->read(dmaSample->readBuffer, dma->captureStore.captureSize,
+                     timeoutMs, &remainingBytes, status);
+
+  *remainingOut = remainingBytes / dma->captureStore.captureSize;
+
+  if (*status == 0) {
+    uint32_t lower_sample =
+        dmaSample->readBuffer[dma->captureStore.captureSize - 1];
+    dmaSample->timeStamp = HAL_ExpandFPGATime(lower_sample, status);
+    if (*status != 0) {
+      return HAL_DMA_ERROR;
+    }
+    dmaSample->triggerChannels = dma->captureStore.triggerChannels;
+    dmaSample->captureSize = dma->captureStore.captureSize;
+    std::memcpy(dmaSample->channelOffsets, dma->captureStore.channelOffsets,
+                sizeof(dmaSample->channelOffsets));
+    return HAL_DMA_OK;
+  } else if (*status == NiFpga_Status_FifoTimeout) {
+    *status = 0;
+    return HAL_DMA_TIMEOUT;
+  } else {
+    return HAL_DMA_ERROR;
+  }
+}
+
+enum HAL_DMAReadStatus HAL_ReadDMA(HAL_DMAHandle handle,
+                                   HAL_DMASample* dmaSample, int32_t timeoutMs,
+                                   int32_t* remainingOut, int32_t* status) {
+  auto dma = dmaHandles->Get(handle);
+  if (!dma) {
+    *status = HAL_HANDLE_ERROR;
+    return HAL_DMA_ERROR;
+  }
+
+  return HAL_ReadDMADirect(dma.get(), dmaSample, timeoutMs, remainingOut,
+                           status);
+}
+
+static uint32_t ReadDMAValue(const HAL_DMASample& dma, int valueType, int index,
+                             int32_t* status) {
+  auto offset = dma.channelOffsets[valueType];
+  if (offset == -1) {
+    *status = NiFpga_Status_ResourceNotFound;
+    return 0;
+  }
+  return dma.readBuffer[offset + index];
+}
+
+uint64_t HAL_GetDMASampleTime(const HAL_DMASample* dmaSample, int32_t* status) {
+  return dmaSample->timeStamp;
+}
+
+int32_t HAL_GetDMASampleEncoderRaw(const HAL_DMASample* dmaSample,
+                                   HAL_EncoderHandle encoderHandle,
+                                   int32_t* status) {
+  HAL_FPGAEncoderHandle fpgaEncoderHandle = 0;
+  HAL_CounterHandle counterHandle = 0;
+  bool validEncoderHandle = hal::GetEncoderBaseHandle(
+      encoderHandle, &fpgaEncoderHandle, &counterHandle);
+
+  if (!validEncoderHandle) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  if (counterHandle != HAL_kInvalidHandle) {
+    return HAL_GetDMASampleCounter(dmaSample, counterHandle, status);
+  }
+
+  if (getHandleType(fpgaEncoderHandle) != HAL_HandleEnum::FPGAEncoder) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  int32_t index = getHandleIndex(fpgaEncoderHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  uint32_t dmaWord = 0;
+  *status = 0;
+  if (index < 4) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_Encoders_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_Encoders_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return -1;
+  }
+
+  return static_cast<int32_t>(dmaWord) >> 1;
+}
+
+int32_t HAL_GetDMASampleEncoderPeriodRaw(const HAL_DMASample* dmaSample,
+                                         HAL_EncoderHandle encoderHandle,
+                                         int32_t* status) {
+  HAL_FPGAEncoderHandle fpgaEncoderHandle = 0;
+  HAL_CounterHandle counterHandle = 0;
+  bool validEncoderHandle = hal::GetEncoderBaseHandle(
+      encoderHandle, &fpgaEncoderHandle, &counterHandle);
+
+  if (!validEncoderHandle) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  if (counterHandle != HAL_kInvalidHandle) {
+    return HAL_GetDMASampleCounterPeriod(dmaSample, counterHandle, status);
+  }
+
+  if (getHandleType(fpgaEncoderHandle) != HAL_HandleEnum::FPGAEncoder) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  int32_t index = getHandleIndex(fpgaEncoderHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  uint32_t dmaWord = 0;
+  *status = 0;
+  if (index < 4) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_EncoderTimers_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_EncoderTimers_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return -1;
+  }
+
+  return static_cast<int32_t>(dmaWord) & 0x7FFFFF;
+}
+
+int32_t HAL_GetDMASampleCounter(const HAL_DMASample* dmaSample,
+                                HAL_CounterHandle counterHandle,
+                                int32_t* status) {
+  if (getHandleType(counterHandle) != HAL_HandleEnum::Counter) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  int32_t index = getHandleIndex(counterHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  uint32_t dmaWord = 0;
+  *status = 0;
+  if (index < 4) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_Counters_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_Counters_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return -1;
+  }
+
+  return static_cast<int32_t>(dmaWord) >> 1;
+}
+
+int32_t HAL_GetDMASampleCounterPeriod(const HAL_DMASample* dmaSample,
+                                      HAL_CounterHandle counterHandle,
+                                      int32_t* status) {
+  if (getHandleType(counterHandle) != HAL_HandleEnum::Counter) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  int32_t index = getHandleIndex(counterHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  uint32_t dmaWord = 0;
+  *status = 0;
+  if (index < 4) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_CounterTimers_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_CounterTimers_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return -1;
+  }
+
+  return static_cast<int32_t>(dmaWord) & 0x7FFFFF;
+}
+
+HAL_Bool HAL_GetDMASampleDigitalSource(const HAL_DMASample* dmaSample,
+                                       HAL_Handle dSourceHandle,
+                                       int32_t* status) {
+  HAL_HandleEnum handleType = getHandleType(dSourceHandle);
+  int32_t index = getHandleIndex(dSourceHandle);
+
+  *status = 0;
+  if (handleType == HAL_HandleEnum::DIO) {
+    auto readVal = ReadDMAValue(*dmaSample, kEnable_DI, 0, status);
+    if (*status == 0) {
+      if (index < kNumDigitalHeaders) {
+        return (readVal >> index) & 0x1;
+      } else {
+        return (readVal >> (index + 6)) & 0x1;
+      }
+    }
+  } else if (handleType == HAL_HandleEnum::AnalogTrigger) {
+    auto readVal = ReadDMAValue(*dmaSample, kEnable_AnalogTriggers, 0, status);
+    if (*status == 0) {
+      return (readVal >> index) & 0x1;
+    }
+  } else {
+    *status = NiFpga_Status_InvalidParameter;
+  }
+  return false;
+}
+int32_t HAL_GetDMASampleAnalogInputRaw(const HAL_DMASample* dmaSample,
+                                       HAL_AnalogInputHandle aInHandle,
+                                       int32_t* status) {
+  if (getHandleType(aInHandle) != HAL_HandleEnum::AnalogInput) {
+    *status = HAL_HANDLE_ERROR;
+    return 0xFFFFFFFF;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return 0xFFFFFFFF;
+  }
+
+  uint32_t dmaWord = 0;
+  if (index < 4) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_AI0_Low, index / 2, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_AI0_High, (index - 4) / 2, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return 0xFFFFFFFF;
+  }
+
+  if (index % 2) {
+    return (dmaWord >> 16) & 0xffff;
+  } else {
+    return dmaWord & 0xffff;
+  }
+}
+
+int32_t HAL_GetDMASampleAveragedAnalogInputRaw(const HAL_DMASample* dmaSample,
+                                               HAL_AnalogInputHandle aInHandle,
+                                               int32_t* status) {
+  if (getHandleType(aInHandle) != HAL_HandleEnum::AnalogInput) {
+    *status = HAL_HANDLE_ERROR;
+    return 0xFFFFFFFF;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return 0xFFFFFFFF;
+  }
+
+  uint32_t dmaWord = 0;
+  if (index < 4) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_AIAveraged0_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_AIAveraged0_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return 0xFFFFFFFF;
+  }
+
+  return dmaWord;
+}
+
+void HAL_GetDMASampleAnalogAccumulator(const HAL_DMASample* dmaSample,
+                                       HAL_AnalogInputHandle aInHandle,
+                                       int64_t* count, int64_t* value,
+                                       int32_t* status) {
+  if (!HAL_IsAccumulatorChannel(aInHandle, status)) {
+    *status = HAL_INVALID_ACCUMULATOR_CHANNEL;
+    return;
+  }
+
+  int32_t index = getHandleIndex(aInHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return;
+  }
+
+  uint32_t dmaWord = 0;
+  uint32_t dmaValue1 = 0;
+  uint32_t dmaValue2 = 0;
+  if (index == 0) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_Accumulator0, index, status);
+    dmaValue1 =
+        ReadDMAValue(*dmaSample, kEnable_Accumulator0, index + 1, status);
+    dmaValue2 =
+        ReadDMAValue(*dmaSample, kEnable_Accumulator0, index + 2, status);
+  } else if (index == 1) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_Accumulator1, index - 1, status);
+    dmaValue1 = ReadDMAValue(*dmaSample, kEnable_Accumulator0, index, status);
+    dmaValue2 =
+        ReadDMAValue(*dmaSample, kEnable_Accumulator0, index + 1, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return;
+  }
+
+  *count = dmaWord;
+
+  *value = static_cast<int64_t>(dmaValue1) << 32 | dmaValue2;
+}
+
+int32_t HAL_GetDMASampleDutyCycleOutputRaw(const HAL_DMASample* dmaSample,
+                                           HAL_DutyCycleHandle dutyCycleHandle,
+                                           int32_t* status) {
+  if (getHandleType(dutyCycleHandle) != HAL_HandleEnum::DutyCycle) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  int32_t index = getHandleIndex(dutyCycleHandle);
+  if (index < 0) {
+    *status = HAL_HANDLE_ERROR;
+    return -1;
+  }
+
+  uint32_t dmaWord = 0;
+  *status = 0;
+  if (index < 4) {
+    dmaWord = ReadDMAValue(*dmaSample, kEnable_DutyCycle_Low, index, status);
+  } else if (index < 8) {
+    dmaWord =
+        ReadDMAValue(*dmaSample, kEnable_DutyCycle_High, index - 4, status);
+  } else {
+    *status = NiFpga_Status_ResourceNotFound;
+  }
+  if (*status != 0) {
+    return -1;
+  }
+  return dmaWord;
+}
+}  // extern "C"