Remove stupid wpilib forwarding target
It's silly to need to use NO_BUILD_AMD64 for //aos/...
Change-Id: I103b3c3a0f5cfb72c081c5aafef624cf0533acb0
diff --git a/frc971/wpilib/dma.cc b/frc971/wpilib/dma.cc
new file mode 100644
index 0000000..ea9f64c
--- /dev/null
+++ b/frc971/wpilib/dma.cc
@@ -0,0 +1,484 @@
+#include "frc971/wpilib/dma.h"
+
+#include <string.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include "DigitalSource.h"
+#include "AnalogInput.h"
+#include "Encoder.h"
+
+// Interface to the roboRIO FPGA's DMA features.
+
+// Like tEncoder::tOutput with the bitfields reversed.
+typedef union {
+ struct {
+ unsigned Direction: 1;
+ signed Value: 31;
+ };
+ struct {
+ unsigned value: 32;
+ };
+} t1Output;
+
+static const uint32_t kNumHeaders = 10;
+
+#ifdef WPILIB2015
+static constexpr ssize_t kChannelSize[18] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
+ 2, 1, 4, 4, 4, 4, 4, 4};
+#else
+static constexpr ssize_t kChannelSize[20] = {2, 2, 4, 4, 2, 2, 4, 4, 3, 3,
+ 2, 1, 4, 4, 4, 4, 4, 4, 4, 4};
+#endif
+
+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,
+#ifdef WPILIB2015
+ kEnable_Encoders = 16,
+ kEnable_EncoderTimers = 17,
+#else
+ kEnable_Encoders_Low = 16,
+ kEnable_Encoders_High = 17,
+ kEnable_EncoderTimers_Low = 18,
+ kEnable_EncoderTimers_High = 19,
+#endif
+};
+
+DMA::DMA() {
+ tRioStatusCode status = 0;
+ tdma_config_ = tDMA::create(&status);
+ tdma_config_->writeConfig_ExternalClock(false, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+#ifdef WPILIB2015
+ NiFpga_WriteU32(0x10000, 0x1832c, 0x0);
+#endif
+ if (status != 0) {
+ return;
+ }
+ SetRate(1);
+ SetPause(false);
+}
+
+DMA::~DMA() {
+ tRioStatusCode status = 0;
+
+ manager_->stop(&status);
+ delete tdma_config_;
+}
+
+void DMA::SetPause(bool pause) {
+ tRioStatusCode status = 0;
+ tdma_config_->writeConfig_Pause(pause, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetRate(uint32_t cycles) {
+ if (cycles < 1) {
+ cycles = 1;
+ }
+ tRioStatusCode status = 0;
+ tdma_config_->writeRate(cycles, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(Encoder *encoder) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+ const int index = encoder->GetFPGAIndex();
+
+#ifdef WPILIB2015
+ if (index < 4) {
+ // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+ tdma_config_->writeConfig_Enable_Encoders(true, &status);
+ } else {
+ wpi_setErrorWithContext(
+ NiFpga_Status_InvalidParameter,
+ "FPGA encoder index is not in the 4 that get logged.");
+ return;
+ }
+#else
+ if (index < 4) {
+ // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+ tdma_config_->writeConfig_Enable_Encoders_Low(true, &status);
+ } else if (index < 8) {
+ // TODO(austin): Encoder uses a Counter for 1x or 2x; quad for 4x...
+ tdma_config_->writeConfig_Enable_Encoders_High(true, &status);
+ } else {
+ wpi_setErrorWithContext(
+ NiFpga_Status_InvalidParameter,
+ "FPGA encoder index is not in the 4 that get logged.");
+ return;
+ }
+#endif
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(DigitalSource * /*input*/) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+
+ tdma_config_->writeConfig_Enable_DI(true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::Add(AnalogInput *input) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Add() only works before DMA::Start()");
+ return;
+ }
+
+ if (input->GetChannel() <= 3) {
+ tdma_config_->writeConfig_Enable_AI0_Low(true, &status);
+ } else {
+ tdma_config_->writeConfig_Enable_AI0_High(true, &status);
+ }
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+}
+
+void DMA::SetExternalTrigger(DigitalSource *input, bool rising, bool falling) {
+ tRioStatusCode status = 0;
+
+ if (manager_) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::SetExternalTrigger() only works before DMA::Start()");
+ return;
+ }
+
+ auto index =
+ ::std::find(trigger_channels_.begin(), trigger_channels_.end(), false);
+ if (index == trigger_channels_.end()) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA: No channels left");
+ return;
+ }
+ *index = true;
+
+ const int channel_index = ::std::distance(trigger_channels_.begin(), index);
+
+ const bool is_external_clock =
+ tdma_config_->readConfig_ExternalClock(&status);
+ if (status == 0) {
+ if (!is_external_clock) {
+ tdma_config_->writeConfig_ExternalClock(true, &status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ }
+ } else {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return;
+ }
+
+ nFPGA::nRoboRIO_FPGANamespace::tDMA::tExternalTriggers new_trigger;
+
+ new_trigger.FallingEdge = falling;
+ new_trigger.RisingEdge = rising;
+ new_trigger.ExternalClockSource_AnalogTrigger = false;
+ unsigned char module = 0;
+ unsigned char channel = input->GetChannelForRouting();
+ if (channel >= kNumHeaders) {
+ module = 1;
+ channel -= kNumHeaders;
+ } else {
+ module = 0;
+ }
+
+ new_trigger.ExternalClockSource_Module = module;
+ new_trigger.ExternalClockSource_Channel = channel;
+
+// Configures the trigger to be external, not off the FPGA clock.
+#ifndef WPILIB2015
+ tdma_config_->writeExternalTriggers(channel_index / 4, channel_index % 4,
+ new_trigger, &status);
+ if (status != 0) {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return;
+ }
+#else
+ uint32_t current_triggers;
+ tRioStatusCode register_status =
+ NiFpga_ReadU32(0x10000, 0x1832c, ¤t_triggers);
+ if (register_status != 0) {
+ wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+ return;
+ }
+ current_triggers = (current_triggers & ~(0xff << (channel_index * 8))) |
+ (new_trigger.value << (channel_index * 8));
+ register_status = NiFpga_WriteU32(0x10000, 0x1832c, current_triggers);
+ if (register_status != 0) {
+ wpi_setErrorWithContext(register_status, getHALErrorMessage(status));
+ return;
+ }
+#endif
+}
+
+DMA::ReadStatus DMA::Read(DMASample *sample, uint32_t timeout_ms,
+ size_t *remaining_out) {
+ tRioStatusCode status = 0;
+ size_t remainingBytes = 0;
+ *remaining_out = 0;
+
+ if (!manager_.get()) {
+ wpi_setErrorWithContext(NiFpga_Status_InvalidParameter,
+ "DMA::Read() only works after DMA::Start()");
+ return STATUS_ERROR;
+ }
+
+ sample->dma_ = this;
+ manager_->read(sample->read_buffer_, capture_size_, timeout_ms,
+ &remainingBytes, &status);
+
+ // TODO(jerry): Do this only if status == 0?
+ *remaining_out = remainingBytes / capture_size_;
+
+ // TODO(austin): Check that *remainingBytes % capture_size_ == 0 and deal
+ // with it if it isn't. Probably meant that we overflowed?
+ if (status == 0) {
+ return STATUS_OK;
+ } else if (status == NiFpga_Status_FifoTimeout) {
+ return STATUS_TIMEOUT;
+ } else {
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ return STATUS_ERROR;
+ }
+}
+
+const char *DMA::NameOfReadStatus(ReadStatus s) {
+ switch (s) {
+ case STATUS_OK: return "OK";
+ case STATUS_TIMEOUT: return "TIMEOUT";
+ case STATUS_ERROR: return "ERROR";
+ default: return "(bad ReadStatus code)";
+ }
+}
+
+void DMA::Start(size_t queue_depth) {
+ tRioStatusCode status = 0;
+ tconfig_ = tdma_config_->readConfig(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+
+ {
+ size_t accum_size = 0;
+#define SET_SIZE(bit) \
+ if (tconfig_.bit) { \
+ channel_offsets_[k##bit] = accum_size; \
+ accum_size += kChannelSize[k##bit]; \
+ } else { \
+ channel_offsets_[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);
+#ifdef WPILIB2015
+ SET_SIZE(Enable_Encoders);
+ SET_SIZE(Enable_EncoderTimers);
+#else
+ SET_SIZE(Enable_Encoders_Low);
+ SET_SIZE(Enable_Encoders_High);
+ SET_SIZE(Enable_EncoderTimers_Low);
+ SET_SIZE(Enable_EncoderTimers_High);
+#endif
+#undef SET_SIZE
+ capture_size_ = accum_size + 1;
+ }
+
+ manager_.reset(
+ new nFPGA::tDMAManager(0, queue_depth * capture_size_, &status));
+
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ // Start, stop, start to clear the buffer.
+ manager_->start(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ manager_->stop(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+ manager_->start(&status);
+ wpi_setErrorWithContext(status, getHALErrorMessage(status));
+ if (status != 0) {
+ return;
+ }
+
+}
+
+static_assert(::std::is_pod<DMASample>::value, "DMASample needs to be POD");
+
+ssize_t DMASample::offset(int index) const { return dma_->channel_offsets_[index]; }
+
+uint32_t DMASample::GetTime() const {
+ return read_buffer_[dma_->capture_size_ - 1];
+}
+
+double DMASample::GetTimestamp() const {
+ return static_cast<double>(GetTime()) * 0.000001;
+}
+
+bool DMASample::Get(DigitalSource *input) const {
+ if (offset(kEnable_DI) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return false;
+ }
+ if (input->GetChannelForRouting() < kNumHeaders) {
+ return (read_buffer_[offset(kEnable_DI)] >> input->GetChannelForRouting()) &
+ 0x1;
+ } else {
+ return (read_buffer_[offset(kEnable_DI)] >>
+ (input->GetChannelForRouting() + 6)) &
+ 0x1;
+ }
+}
+
+int32_t DMASample::GetRaw(Encoder *input) const {
+ int index = input->GetFPGAIndex();
+ uint32_t dmaWord = 0;
+#ifdef WPILIB2015
+ if (index >= 4 || offset(kEnable_Encoders) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return -1;
+ }
+ dmaWord = read_buffer_[offset(kEnable_Encoders) + index];
+#else
+ if (index < 4) {
+ if (offset(kEnable_Encoders_Low) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return -1;
+ }
+ dmaWord = read_buffer_[offset(kEnable_Encoders_Low) + index];
+ } else if (index < 8) {
+ if (offset(kEnable_Encoders_High) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return -1;
+ }
+ dmaWord = read_buffer_[offset(kEnable_Encoders_High) + (index - 4)];
+ } else {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return 0;
+ }
+#endif
+
+ int32_t result = 0;
+
+ // Extract the 31-bit signed tEncoder::tOutput Value using a struct with the
+ // reverse packed field order of tOutput. This gets Value from the high
+ // order 31 bits of output on little-endian ARM using gcc. This works
+ // even though C/C++ doesn't guarantee bitfield order.
+ t1Output output;
+
+ output.value = dmaWord;
+ result = output.Value;
+
+ return result;
+}
+
+int32_t DMASample::Get(Encoder *input) const {
+ int32_t raw = GetRaw(input);
+
+ return raw / input->GetEncodingScale();
+}
+
+uint16_t DMASample::GetValue(AnalogInput *input) const {
+ uint32_t channel = input->GetChannel();
+ uint32_t dmaWord;
+ if (channel < 4) {
+ if (offset(kEnable_AI0_Low) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return 0xffff;
+ }
+ dmaWord = read_buffer_[offset(kEnable_AI0_Low) + channel / 2];
+ } else if (channel < 8) {
+ if (offset(kEnable_AI0_High) == -1) {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return 0xffff;
+ }
+ dmaWord = read_buffer_[offset(kEnable_AI0_High) + (channel - 4) / 2];
+ } else {
+ wpi_setStaticErrorWithContext(
+ dma_, NiFpga_Status_ResourceNotFound,
+ getHALErrorMessage(NiFpga_Status_ResourceNotFound));
+ return 0xffff;
+ }
+ if (channel % 2) {
+ return (dmaWord >> 16) & 0xffff;
+ } else {
+ return dmaWord & 0xffff;
+ }
+ return static_cast<int16_t>(dmaWord);
+}
+
+float DMASample::GetVoltage(AnalogInput *input) const {
+ uint16_t value = GetValue(input);
+ if (value == 0xffff) return 0.0;
+ uint32_t lsb_weight = input->GetLSBWeight();
+ int32_t offset = input->GetOffset();
+ float voltage = lsb_weight * 1.0e-9 * value - offset * 1.0e-9;
+ return voltage;
+}