diff --git a/hal/src/main/native/athena/AnalogInput.cpp b/hal/src/main/native/athena/AnalogInput.cpp index 859524ec78..9002aea994 100644 --- a/hal/src/main/native/athena/AnalogInput.cpp +++ b/hal/src/main/native/athena/AnalogInput.cpp @@ -201,6 +201,14 @@ double HAL_GetAnalogVoltage(HAL_AnalogInputHandle analogPortHandle, return voltage; } +double HAL_GetAnalogValueToVolts(HAL_AnalogInputHandle analogPortHandle, + int32_t rawValue, int32_t* status) { + int32_t LSBWeight = HAL_GetAnalogLSBWeight(analogPortHandle, status); + int32_t offset = HAL_GetAnalogOffset(analogPortHandle, status); + double voltage = LSBWeight * 1.0e-9 * rawValue - offset * 1.0e-9; + return voltage; +} + double HAL_GetAnalogAverageVoltage(HAL_AnalogInputHandle analogPortHandle, int32_t* status) { int32_t value = HAL_GetAnalogAverageValue(analogPortHandle, status); diff --git a/hal/src/main/native/athena/DMA.cpp b/hal/src/main/native/athena/DMA.cpp new file mode 100644 index 0000000000..fd83d7fdb3 --- /dev/null +++ b/hal/src/main/native/athena/DMA.cpp @@ -0,0 +1,1004 @@ +/*----------------------------------------------------------------------------*/ +/* 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 +#include +#include +#include + +#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_pod_v, "DMA Sample must be POD"); + +namespace { + +struct DMA { + std::unique_ptr manager; + std::unique_ptr 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* + dmaHandles; + +namespace hal { +namespace init { +void InitializeDMA() { + static hal::LimitedHandleResource + 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(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( + 1, 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(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(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(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(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(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(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" diff --git a/hal/src/main/native/athena/Encoder.cpp b/hal/src/main/native/athena/Encoder.cpp index bf3a273e89..02426342ec 100644 --- a/hal/src/main/native/athena/Encoder.cpp +++ b/hal/src/main/native/athena/Encoder.cpp @@ -238,6 +238,19 @@ void InitializeEncoder() { } // namespace init } // namespace hal +namespace hal { +bool GetEncoderBaseHandle(HAL_EncoderHandle handle, + HAL_FPGAEncoderHandle* fpgaHandle, + HAL_CounterHandle* counterHandle) { + auto encoder = encoderHandles->Get(handle); + if (!handle) return false; + + *fpgaHandle = encoder->m_encoder; + *counterHandle = encoder->m_counter; + return true; +} +} // namespace hal + extern "C" { HAL_EncoderHandle HAL_InitializeEncoder( HAL_Handle digitalSourceHandleA, HAL_AnalogTriggerType analogTriggerTypeA, diff --git a/hal/src/main/native/athena/EncoderInternal.h b/hal/src/main/native/athena/EncoderInternal.h index 1e7ed4d70e..bed4ee3443 100644 --- a/hal/src/main/native/athena/EncoderInternal.h +++ b/hal/src/main/native/athena/EncoderInternal.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2016-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. */ @@ -13,8 +13,16 @@ namespace hal { +bool GetEncoderBaseHandle(HAL_EncoderHandle handle, + HAL_FPGAEncoderHandle* fpgaEncoderHandle, + HAL_CounterHandle* counterHandle); + class Encoder { public: + friend bool GetEncoderBaseHandle(HAL_EncoderHandle handle, + HAL_FPGAEncoderHandle* fpgaEncoderHandle, + HAL_CounterHandle* counterHandle); + Encoder(HAL_Handle digitalSourceHandleA, HAL_AnalogTriggerType analogTriggerTypeA, HAL_Handle digitalSourceHandleB, diff --git a/hal/src/main/native/athena/HAL.cpp b/hal/src/main/native/athena/HAL.cpp index 5c47e0dfe3..804d68ac75 100644 --- a/hal/src/main/native/athena/HAL.cpp +++ b/hal/src/main/native/athena/HAL.cpp @@ -56,6 +56,7 @@ void InitializeHAL() { InitializeCounter(); InitializeDigitalInternal(); InitializeDIO(); + InitializeDMA(); InitializeDutyCycle(); InitializeEncoder(); InitializeFPGAEncoder(); @@ -256,6 +257,28 @@ uint64_t HAL_GetFPGATime(int32_t* status) { return (upper2 << 32) + lower; } +uint64_t HAL_ExpandFPGATime(uint32_t unexpanded_lower, int32_t* status) { + // Capture the current FPGA time. This will give us the upper half of the + // clock. + uint64_t fpga_time = HAL_GetFPGATime(status); + if (*status != 0) return 0; + + // Now, we need to detect the case where the lower bits rolled over after we + // sampled. In that case, the upper bits will be 1 bigger than they should + // be. + + // Break it into lower and upper portions. + uint32_t lower = fpga_time & 0xffffffffull; + uint64_t upper = (fpga_time >> 32) & 0xffffffff; + + // The time was sampled *before* the current time, so roll it back. + if (lower < unexpanded_lower) { + --upper; + } + + return (upper << 32) + static_cast(unexpanded_lower); +} + HAL_Bool HAL_GetFPGAButton(int32_t* status) { if (!global) { *status = NiFpga_Status_ResourceNotInitialized; diff --git a/hal/src/main/native/athena/HALInitializer.h b/hal/src/main/native/athena/HALInitializer.h index 22c1dcb705..12640a8d4e 100644 --- a/hal/src/main/native/athena/HALInitializer.h +++ b/hal/src/main/native/athena/HALInitializer.h @@ -32,6 +32,7 @@ extern void InitializeConstants(); extern void InitializeCounter(); extern void InitializeDigitalInternal(); extern void InitializeDIO(); +extern void InitializeDMA(); extern void InitializeDutyCycle(); extern void InitializeEncoder(); extern void InitializeFPGAEncoder(); diff --git a/hal/src/main/native/include/hal/AnalogInput.h b/hal/src/main/native/include/hal/AnalogInput.h index c0e45b3633..95ba4e0e2c 100644 --- a/hal/src/main/native/include/hal/AnalogInput.h +++ b/hal/src/main/native/include/hal/AnalogInput.h @@ -233,6 +233,16 @@ int32_t HAL_GetAnalogLSBWeight(HAL_AnalogInputHandle analogPortHandle, */ int32_t HAL_GetAnalogOffset(HAL_AnalogInputHandle analogPortHandle, int32_t* status); + +/** + * Get the analog voltage from a raw value. + * + * @param analogPortHandle Handle to the analog port the values were read from. + * @param rawValue The raw analog value + * @return The voltage relating to the value + */ +double HAL_GetAnalogValueToVolts(HAL_AnalogInputHandle analogPortHandle, + int32_t rawValue, int32_t* status); #ifdef __cplusplus } // extern "C" #endif diff --git a/hal/src/main/native/include/hal/DMA.h b/hal/src/main/native/include/hal/DMA.h new file mode 100644 index 0000000000..38aaca226c --- /dev/null +++ b/hal/src/main/native/include/hal/DMA.h @@ -0,0 +1,129 @@ +/*----------------------------------------------------------------------------*/ +/* 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "hal/AnalogTrigger.h" +#include "hal/Types.h" + +// clang-format off +/** + * The DMA Read Status. + */ +HAL_ENUM(HAL_DMAReadStatus ) { + HAL_DMA_OK = 1, + HAL_DMA_TIMEOUT = 2, + HAL_DMA_ERROR = 3, +}; +// clang-format on + +struct HAL_DMASample { + uint32_t readBuffer[74]; + int32_t channelOffsets[22]; + uint64_t timeStamp; + uint32_t captureSize; + uint8_t triggerChannels; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +HAL_DMAHandle HAL_InitializeDMA(int32_t* status); +void HAL_FreeDMA(HAL_DMAHandle handle); + +void HAL_SetDMAPause(HAL_DMAHandle handle, HAL_Bool pause, int32_t* status); +void HAL_SetDMARate(HAL_DMAHandle handle, int32_t cycles, int32_t* status); + +void HAL_AddDMAEncoder(HAL_DMAHandle handle, HAL_EncoderHandle encoderHandle, + int32_t* status); +void HAL_AddDMAEncoderPeriod(HAL_DMAHandle handle, + HAL_EncoderHandle encoderHandle, int32_t* status); +void HAL_AddDMACounter(HAL_DMAHandle handle, HAL_CounterHandle counterHandle, + int32_t* status); +void HAL_AddDMACounterPeriod(HAL_DMAHandle handle, + HAL_CounterHandle counterHandle, int32_t* status); +void HAL_AddDMADigitalSource(HAL_DMAHandle handle, + HAL_Handle digitalSourceHandle, int32_t* status); +void HAL_AddDMAAnalogInput(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, int32_t* status); + +void HAL_AddDMAAveragedAnalogInput(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, + int32_t* status); + +void HAL_AddDMAAnalogAccumulator(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, + int32_t* status); + +void HAL_AddDMADutyCycle(HAL_DMAHandle handle, + HAL_DutyCycleHandle dutyCycleHandle, int32_t* status); + +void HAL_SetDMAExternalTrigger(HAL_DMAHandle handle, + HAL_Handle digitalSourceHandle, + HAL_AnalogTriggerType analogTriggerType, + HAL_Bool rising, HAL_Bool falling, + int32_t* status); + +void HAL_StartDMA(HAL_DMAHandle handle, int32_t queueDepth, int32_t* status); +void HAL_StopDMA(HAL_DMAHandle handle, int32_t* status); + +void* HAL_GetDMADirectPointer(HAL_DMAHandle handle); + +enum HAL_DMAReadStatus HAL_ReadDMADirect(void* dmaPointer, + HAL_DMASample* dmaSample, + int32_t timeoutMs, + int32_t* remainingOut, + int32_t* status); + +enum HAL_DMAReadStatus HAL_ReadDMA(HAL_DMAHandle handle, + HAL_DMASample* dmaSample, int32_t timeoutMs, + int32_t* remainingOut, int32_t* status); + +// Sampling Code +uint64_t HAL_GetDMASampleTime(const HAL_DMASample* dmaSample, int32_t* status); + +int32_t HAL_GetDMASampleEncoderRaw(const HAL_DMASample* dmaSample, + HAL_EncoderHandle encoderHandle, + int32_t* status); + +int32_t HAL_GetDMASampleCounter(const HAL_DMASample* dmaSample, + HAL_CounterHandle counterHandle, + int32_t* status); + +int32_t HAL_GetDMASampleEncoderPeriodRaw(const HAL_DMASample* dmaSample, + HAL_EncoderHandle encoderHandle, + int32_t* status); + +int32_t HAL_GetDMASampleCounterPeriod(const HAL_DMASample* dmaSample, + HAL_CounterHandle counterHandle, + int32_t* status); +HAL_Bool HAL_GetDMASampleDigitalSource(const HAL_DMASample* dmaSample, + HAL_Handle dSourceHandle, + int32_t* status); +int32_t HAL_GetDMASampleAnalogInputRaw(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int32_t* status); + +int32_t HAL_GetDMASampleAveragedAnalogInputRaw(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int32_t* status); + +void HAL_GetDMASampleAnalogAccumulator(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int64_t* count, int64_t* value, + int32_t* status); + +int32_t HAL_GetDMASampleDutyCycleOutputRaw(const HAL_DMASample* dmaSample, + HAL_DutyCycleHandle dutyCycleHandle, + int32_t* status); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/hal/src/main/native/include/hal/Errors.h b/hal/src/main/native/include/hal/Errors.h index 1f2e68ed6f..b0b3343fa3 100644 --- a/hal/src/main/native/include/hal/Errors.h +++ b/hal/src/main/native/include/hal/Errors.h @@ -94,6 +94,10 @@ #define HAL_HANDLE_ERROR_MESSAGE \ "HAL: A handle parameter was passed incorrectly" +#define HAL_INVALID_DMA_ADDITION -1102 +#define HAL_INVALID_DMA_ADDITION_MESSAGE \ + "HAL_AddDMA() only works before HAL_StartDMA()" + #define HAL_SERIAL_PORT_NOT_FOUND -1123 #define HAL_SERIAL_PORT_NOT_FOUND_MESSAGE \ "HAL: The specified serial port device was not found" diff --git a/hal/src/main/native/include/hal/HALBase.h b/hal/src/main/native/include/hal/HALBase.h index 39fb47ac11..f61d9e42c5 100644 --- a/hal/src/main/native/include/hal/HALBase.h +++ b/hal/src/main/native/include/hal/HALBase.h @@ -109,6 +109,20 @@ HAL_PortHandle HAL_GetPortWithModule(int32_t module, int32_t channel); */ uint64_t HAL_GetFPGATime(int32_t* status); +/** + * Given an 32 bit FPGA time, expand it to the nearest likely 64 bit FPGA time. + * + * Note: This is making the assumption that the timestamp being converted is + * always in the past. If you call this with a future timestamp, it probably + * will make it in the past. If you wait over 70 minutes between capturing the + * bottom 32 bits of the timestamp and expanding it, you will be off by + * multiples of 1<<32 microseconds. + * + * @return The current time in microseconds according to the FPGA (since FPGA + * reset) as a 64 bit number. + */ +uint64_t HAL_ExpandFPGATime(uint32_t unexpanded_lower, int32_t* status); + /** * Call this to start up HAL. This is required for robot programs. * diff --git a/hal/src/main/native/include/hal/Types.h b/hal/src/main/native/include/hal/Types.h index b96f994abf..53bcd6d098 100644 --- a/hal/src/main/native/include/hal/Types.h +++ b/hal/src/main/native/include/hal/Types.h @@ -57,6 +57,8 @@ typedef HAL_Handle HAL_SimDeviceHandle; typedef HAL_Handle HAL_SimValueHandle; +typedef HAL_Handle HAL_DMAHandle; + typedef HAL_Handle HAL_DutyCycleHandle; typedef HAL_CANHandle HAL_PDPHandle; diff --git a/hal/src/main/native/include/hal/handles/HandlesInternal.h b/hal/src/main/native/include/hal/handles/HandlesInternal.h index f722ef43fc..ccaf997050 100644 --- a/hal/src/main/native/include/hal/handles/HandlesInternal.h +++ b/hal/src/main/native/include/hal/handles/HandlesInternal.h @@ -66,7 +66,8 @@ enum class HAL_HandleEnum { SimulationJni = 18, CAN = 19, SerialPort = 20, - DutyCycle = 21 + DutyCycle = 21, + DMA = 22, }; /** diff --git a/hal/src/main/native/sim/AnalogInput.cpp b/hal/src/main/native/sim/AnalogInput.cpp index dc5640350f..8ecde745fd 100644 --- a/hal/src/main/native/sim/AnalogInput.cpp +++ b/hal/src/main/native/sim/AnalogInput.cpp @@ -165,6 +165,15 @@ double HAL_GetAnalogVoltage(HAL_AnalogInputHandle analogPortHandle, return SimAnalogInData[port->channel].voltage; } + +double HAL_GetAnalogValueToVolts(HAL_AnalogInputHandle analogPortHandle, + int32_t rawValue, int32_t* status) { + int32_t LSBWeight = HAL_GetAnalogLSBWeight(analogPortHandle, status); + int32_t offset = HAL_GetAnalogOffset(analogPortHandle, status); + double voltage = LSBWeight * 1.0e-9 * rawValue - offset * 1.0e-9; + return voltage; +} + double HAL_GetAnalogAverageVoltage(HAL_AnalogInputHandle analogPortHandle, int32_t* status) { auto port = analogInputHandles->Get(analogPortHandle); diff --git a/hal/src/main/native/sim/DMA.cpp b/hal/src/main/native/sim/DMA.cpp new file mode 100644 index 0000000000..cea5a292ed --- /dev/null +++ b/hal/src/main/native/sim/DMA.cpp @@ -0,0 +1,124 @@ +/*----------------------------------------------------------------------------*/ +/* 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" + +extern "C" { +HAL_DMAHandle HAL_InitializeDMA(int32_t* status) { return HAL_kInvalidHandle; } +void HAL_FreeDMA(HAL_DMAHandle handle) {} + +void HAL_SetDMAPause(HAL_DMAHandle handle, HAL_Bool pause, int32_t* status) {} +void HAL_SetDMARate(HAL_DMAHandle handle, int32_t cycles, int32_t* status) {} + +void HAL_AddDMAEncoder(HAL_DMAHandle handle, HAL_EncoderHandle encoderHandle, + int32_t* status) {} +void HAL_AddDMAEncoderPeriod(HAL_DMAHandle handle, + HAL_EncoderHandle encoderHandle, int32_t* status) { +} +void HAL_AddDMACounter(HAL_DMAHandle handle, HAL_CounterHandle counterHandle, + int32_t* status) {} +void HAL_AddDMACounterPeriod(HAL_DMAHandle handle, + HAL_CounterHandle counterHandle, int32_t* status) { +} +void HAL_AddDMADigitalSource(HAL_DMAHandle handle, + HAL_Handle digitalSourceHandle, int32_t* status) {} +void HAL_AddDMAAnalogInput(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, int32_t* status) {} + +void HAL_AddDMAAveragedAnalogInput(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, + int32_t* status) {} + +void HAL_AddDMAAnalogAccumulator(HAL_DMAHandle handle, + HAL_AnalogInputHandle aInHandle, + int32_t* status) {} + +void HAL_AddDMADutyCycle(HAL_DMAHandle handle, + HAL_DutyCycleHandle dutyCycleHandle, int32_t* status) { +} + +void HAL_SetDMAExternalTrigger(HAL_DMAHandle handle, + HAL_Handle digitalSourceHandle, + HAL_AnalogTriggerType analogTriggerType, + HAL_Bool rising, HAL_Bool falling, + int32_t* status) {} + +void HAL_StartDMA(HAL_DMAHandle handle, int32_t queueDepth, int32_t* status) {} +void HAL_StopDMA(HAL_DMAHandle handle, int32_t* status) {} + +void* HAL_GetDMADirectPointer(HAL_DMAHandle handle) { return nullptr; } + +enum HAL_DMAReadStatus HAL_ReadDMADirect(void* dmaPointer, + HAL_DMASample* dmaSample, + int32_t timeoutMs, + int32_t* remainingOut, + int32_t* status) { + return HAL_DMA_ERROR; +} + +enum HAL_DMAReadStatus HAL_ReadDMA(HAL_DMAHandle handle, + HAL_DMASample* dmaSample, int32_t timeoutMs, + int32_t* remainingOut, int32_t* status) { + return HAL_DMA_ERROR; +} + +// Sampling Code +uint64_t HAL_GetDMASampleTime(const HAL_DMASample* dmaSample, int32_t* status) { + return 0; +} + +int32_t HAL_GetDMASampleEncoderRaw(const HAL_DMASample* dmaSample, + HAL_EncoderHandle encoderHandle, + int32_t* status) { + return 0; +} + +int32_t HAL_GetDMASampleCounter(const HAL_DMASample* dmaSample, + HAL_CounterHandle counterHandle, + int32_t* status) { + return 0; +} + +int32_t HAL_GetDMASampleEncoderPeriodRaw(const HAL_DMASample* dmaSample, + HAL_EncoderHandle encoderHandle, + int32_t* status) { + return 0; +} + +int32_t HAL_GetDMASampleCounterPeriod(const HAL_DMASample* dmaSample, + HAL_CounterHandle counterHandle, + int32_t* status) { + return 0; +} +HAL_Bool HAL_GetDMASampleDigitalSource(const HAL_DMASample* dmaSample, + HAL_Handle dSourceHandle, + int32_t* status) { + return 0; +} +int32_t HAL_GetDMASampleAnalogInputRaw(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int32_t* status) { + return 0; +} + +int32_t HAL_GetDMASampleAveragedAnalogInputRaw(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int32_t* status) { + return 0; +} + +void HAL_GetDMASampleAnalogAccumulator(const HAL_DMASample* dmaSample, + HAL_AnalogInputHandle aInHandle, + int64_t* count, int64_t* value, + int32_t* status) {} + +int32_t HAL_GetDMASampleDutyCycleOutputRaw(const HAL_DMASample* dmaSample, + HAL_DutyCycleHandle dutyCycleHandle, + int32_t* status) { + return 0; +} +} // extern "C" diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp index 633e243131..9e41251edc 100644 --- a/hal/src/main/native/sim/HAL.cpp +++ b/hal/src/main/native/sim/HAL.cpp @@ -222,6 +222,28 @@ int64_t HAL_GetFPGARevision(int32_t* status) { uint64_t HAL_GetFPGATime(int32_t* status) { return hal::GetFPGATime(); } +uint64_t HAL_ExpandFPGATime(uint32_t unexpanded_lower, int32_t* status) { + // Capture the current FPGA time. This will give us the upper half of the + // clock. + uint64_t fpga_time = HAL_GetFPGATime(status); + if (*status != 0) return 0; + + // Now, we need to detect the case where the lower bits rolled over after we + // sampled. In that case, the upper bits will be 1 bigger than they should + // be. + + // Break it into lower and upper portions. + uint32_t lower = fpga_time & 0xffffffffull; + uint64_t upper = (fpga_time >> 32) & 0xffffffff; + + // The time was sampled *before* the current time, so roll it back. + if (lower < unexpanded_lower) { + --upper; + } + + return (upper << 32) + static_cast(unexpanded_lower); +} + HAL_Bool HAL_GetFPGAButton(int32_t* status) { return SimRoboRioData[0].fpgaButton; } diff --git a/wpilibc/src/main/native/cpp/DMA.cpp b/wpilibc/src/main/native/cpp/DMA.cpp new file mode 100644 index 0000000000..1861555da5 --- /dev/null +++ b/wpilibc/src/main/native/cpp/DMA.cpp @@ -0,0 +1,115 @@ +/*----------------------------------------------------------------------------*/ +/* 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 "frc/DMA.h" + +#include +#include +#include +#include +#include + +#include +#include + +using namespace frc; + +DMA::DMA() { + int32_t status = 0; + dmaHandle = HAL_InitializeDMA(&status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +DMA::~DMA() { HAL_FreeDMA(dmaHandle); } + +void DMA::SetPause(bool pause) { + int32_t status = 0; + HAL_SetDMAPause(dmaHandle, pause, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::SetRate(int cycles) { + int32_t status = 0; + HAL_SetDMARate(dmaHandle, cycles, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddEncoder(const Encoder* encoder) { + int32_t status = 0; + HAL_AddDMAEncoder(dmaHandle, encoder->m_encoder, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddEncoderPeriod(const Encoder* encoder) { + int32_t status = 0; + HAL_AddDMAEncoderPeriod(dmaHandle, encoder->m_encoder, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddCounter(const Counter* counter) { + int32_t status = 0; + HAL_AddDMACounter(dmaHandle, counter->m_counter, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddCounterPeriod(const Counter* counter) { + int32_t status = 0; + HAL_AddDMACounterPeriod(dmaHandle, counter->m_counter, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddDigitalSource(const DigitalSource* digitalSource) { + int32_t status = 0; + HAL_AddDMADigitalSource(dmaHandle, digitalSource->GetPortHandleForRouting(), + &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddDutyCycle(const DutyCycle* dutyCycle) { + int32_t status = 0; + HAL_AddDMADutyCycle(dmaHandle, dutyCycle->m_handle, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddAnalogInput(const AnalogInput* analogInput) { + int32_t status = 0; + HAL_AddDMAAnalogInput(dmaHandle, analogInput->m_port, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddAveragedAnalogInput(const AnalogInput* analogInput) { + int32_t status = 0; + HAL_AddDMAAveragedAnalogInput(dmaHandle, analogInput->m_port, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::AddAnalogAccumulator(const AnalogInput* analogInput) { + int32_t status = 0; + HAL_AddDMAAnalogAccumulator(dmaHandle, analogInput->m_port, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::SetExternalTrigger(DigitalSource* source, bool rising, bool falling) { + int32_t status = 0; + HAL_SetDMAExternalTrigger(dmaHandle, source->GetPortHandleForRouting(), + static_cast( + source->GetAnalogTriggerTypeForRouting()), + rising, falling, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::StartDMA(int queueDepth) { + int32_t status = 0; + HAL_StartDMA(dmaHandle, queueDepth, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void DMA::StopDMA() { + int32_t status = 0; + HAL_StopDMA(dmaHandle, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} diff --git a/wpilibc/src/main/native/include/frc/AnalogInput.h b/wpilibc/src/main/native/include/frc/AnalogInput.h index 6567542356..3d14c4cbae 100644 --- a/wpilibc/src/main/native/include/frc/AnalogInput.h +++ b/wpilibc/src/main/native/include/frc/AnalogInput.h @@ -19,6 +19,8 @@ namespace frc { class SendableBuilder; +class DMA; +class DMASample; /** * Analog input class. @@ -38,6 +40,8 @@ class AnalogInput : public ErrorBase, public SendableHelper { friend class AnalogTrigger; friend class AnalogGyro; + friend class DMA; + friend class DMASample; public: static constexpr int kAccumulatorModuleNumber = 1; diff --git a/wpilibc/src/main/native/include/frc/Counter.h b/wpilibc/src/main/native/include/frc/Counter.h index 50e39486fb..2770a02a4a 100644 --- a/wpilibc/src/main/native/include/frc/Counter.h +++ b/wpilibc/src/main/native/include/frc/Counter.h @@ -21,6 +21,8 @@ namespace frc { class DigitalGlitchFilter; class SendableBuilder; +class DMA; +class DMASample; /** * Class for counting the number of ticks on a digital input channel. @@ -36,6 +38,9 @@ class Counter : public ErrorBase, public CounterBase, public Sendable, public SendableHelper { + friend class DMA; + friend class DMASample; + public: enum Mode { kTwoPulse = 0, diff --git a/wpilibc/src/main/native/include/frc/DMA.h b/wpilibc/src/main/native/include/frc/DMA.h new file mode 100644 index 0000000000..57cdd273d1 --- /dev/null +++ b/wpilibc/src/main/native/include/frc/DMA.h @@ -0,0 +1,57 @@ +/*----------------------------------------------------------------------------*/ +/* 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "frc/ErrorBase.h" + +namespace frc { +class Encoder; +class Counter; +class DigitalSource; +class DutyCycle; +class AnalogInput; +class DMASample; + +class DMA : public ErrorBase { + friend class DMASample; + + public: + DMA(); + ~DMA() override; + + DMA& operator=(DMA&& other) = default; + DMA(DMA&& other) = default; + + void SetPause(bool pause); + void SetRate(int cycles); + + void AddEncoder(const Encoder* encoder); + void AddEncoderPeriod(const Encoder* encoder); + + void AddCounter(const Counter* counter); + void AddCounterPeriod(const Counter* counter); + + void AddDigitalSource(const DigitalSource* digitalSource); + + void AddDutyCycle(const DutyCycle* digitalSource); + + void AddAnalogInput(const AnalogInput* analogInput); + void AddAveragedAnalogInput(const AnalogInput* analogInput); + void AddAnalogAccumulator(const AnalogInput* analogInput); + + void SetExternalTrigger(DigitalSource* source, bool rising, bool falling); + + void StartDMA(int queueDepth); + void StopDMA(); + + private: + hal::Handle dmaHandle; +}; +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/DMASample.h b/wpilibc/src/main/native/include/frc/DMASample.h new file mode 100644 index 0000000000..346a161e3d --- /dev/null +++ b/wpilibc/src/main/native/include/frc/DMASample.h @@ -0,0 +1,108 @@ +/*----------------------------------------------------------------------------*/ +/* 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "frc/AnalogInput.h" +#include "frc/Counter.h" +#include "frc/DMA.h" +#include "frc/DutyCycle.h" +#include "frc/Encoder.h" + +namespace frc { +class DMASample : public HAL_DMASample { + public: + HAL_DMAReadStatus Update(const DMA* dma, units::second_t timeout, + int32_t* remaining, int32_t* status) { + units::millisecond_t ms = timeout; + auto timeoutMs = ms.to(); + return HAL_ReadDMA(dma->dmaHandle, this, timeoutMs, remaining, status); + } + + uint64_t GetTime() const { return timeStamp; } + + units::second_t GetTimeStamp() const { + return units::second_t{static_cast(GetTime()) * 1.0e-6}; + } + + int32_t GetEncoderRaw(const Encoder* encoder, int32_t* status) const { + return HAL_GetDMASampleEncoderRaw(this, encoder->m_encoder, status); + } + + double GetEncoderDistance(const Encoder* encoder, int32_t* status) const { + double val = GetEncoderRaw(encoder, status); + val *= encoder->DecodingScaleFactor(); + val *= encoder->GetDistancePerPulse(); + return val; + } + + int32_t GetEncoderPeriodRaw(const Encoder* encoder, int32_t* status) const { + return HAL_GetDMASampleEncoderPeriodRaw(this, encoder->m_encoder, status); + } + + int32_t GetCounter(const Counter* counter, int32_t* status) const { + return HAL_GetDMASampleCounter(this, counter->m_counter, status); + } + + int32_t GetCounterPeriod(const Counter* counter, int32_t* status) const { + return HAL_GetDMASampleCounterPeriod(this, counter->m_counter, status); + } + + bool GetDigitalSource(const DigitalSource* digitalSource, + int32_t* status) const { + return HAL_GetDMASampleDigitalSource( + this, digitalSource->GetPortHandleForRouting(), status); + } + + int32_t GetAnalogInputRaw(const AnalogInput* analogInput, + int32_t* status) const { + return HAL_GetDMASampleAnalogInputRaw(this, analogInput->m_port, status); + } + + double GetAnalogInputVoltage(const AnalogInput* analogInput, + int32_t* status) { + return HAL_GetAnalogValueToVolts( + analogInput->m_port, GetAnalogInputRaw(analogInput, status), status); + } + + int32_t GetAveragedAnalogInputRaw(const AnalogInput* analogInput, + int32_t* status) const { + return HAL_GetDMASampleAveragedAnalogInputRaw(this, analogInput->m_port, + status); + } + + double GetAveragedAnalogInputVoltage(const AnalogInput* analogInput, + int32_t* status) { + return HAL_GetAnalogValueToVolts( + analogInput->m_port, GetAveragedAnalogInputRaw(analogInput, status), + status); + } + + void GetAnalogAccumulator(const AnalogInput* analogInput, int64_t* count, + int64_t* value, int32_t* status) const { + return HAL_GetDMASampleAnalogAccumulator(this, analogInput->m_port, count, + value, status); + } + + int32_t GetDutyCycleOutputRaw(const DutyCycle* dutyCycle, + int32_t* status) const { + return HAL_GetDMASampleDutyCycleOutputRaw(this, dutyCycle->m_handle, + status); + } + + double GetDutyCycleOutput(const DutyCycle* dutyCycle, int32_t* status) { + return GetDutyCycleOutputRaw(dutyCycle, status) / + static_cast(dutyCycle->GetOutputScaleFactor()); + } +}; + +static_assert(std::is_pod_v, "DMA Sample MUST Be POD"); +} // namespace frc diff --git a/wpilibc/src/main/native/include/frc/DutyCycle.h b/wpilibc/src/main/native/include/frc/DutyCycle.h index 1233ee0ca3..f0fc2d8006 100644 --- a/wpilibc/src/main/native/include/frc/DutyCycle.h +++ b/wpilibc/src/main/native/include/frc/DutyCycle.h @@ -18,6 +18,8 @@ namespace frc { class DigitalSource; class AnalogTrigger; +class DMA; +class DMASample; /** * Class to read a duty cycle PWM input. @@ -34,6 +36,8 @@ class DutyCycle : public ErrorBase, public Sendable, public SendableHelper { friend class AnalogTrigger; + friend class DMA; + friend class DMASample; public: /** diff --git a/wpilibc/src/main/native/include/frc/Encoder.h b/wpilibc/src/main/native/include/frc/Encoder.h index 074cc5e16f..60552f62d1 100644 --- a/wpilibc/src/main/native/include/frc/Encoder.h +++ b/wpilibc/src/main/native/include/frc/Encoder.h @@ -23,6 +23,8 @@ namespace frc { class DigitalSource; class DigitalGlitchFilter; class SendableBuilder; +class DMA; +class DMASample; /** * Class to read quad encoders. @@ -44,6 +46,9 @@ class Encoder : public ErrorBase, public PIDSource, public Sendable, public SendableHelper { + friend class DMA; + friend class DMASample; + public: enum IndexingType { kResetWhileHigh,