aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/include/uhd/rfnoc/defaults.hpp15
-rw-r--r--host/include/uhd/rfnoc/fft_block_control.hpp141
-rw-r--r--host/lib/rfnoc/fft_block_control.cpp150
3 files changed, 250 insertions, 56 deletions
diff --git a/host/include/uhd/rfnoc/defaults.hpp b/host/include/uhd/rfnoc/defaults.hpp
index e44e3fa49..e8f1c11e7 100644
--- a/host/include/uhd/rfnoc/defaults.hpp
+++ b/host/include/uhd/rfnoc/defaults.hpp
@@ -72,13 +72,14 @@ static const device_type_t N320 = 0x1320;
static const device_type_t X300 = 0xA300;
// block identifiers
-static const noc_id_t ADDSUB_BLOCK = 0xADD00000;
-static const noc_id_t DUC_BLOCK = 0xD0C00000;
-static const noc_id_t DDC_BLOCK = 0xDDC00000;
-static const noc_id_t FIR_FILTER_BLOCK = 0xf1120000;
-static const noc_id_t FOSPHOR_BLOCK = 0x666F0000;
+static const noc_id_t ADDSUB_BLOCK = 0xADD00000;
+static const noc_id_t DUC_BLOCK = 0xD0C00000;
+static const noc_id_t DDC_BLOCK = 0xDDC00000;
+static const noc_id_t FFT_BLOCK = 0xFF700000;
+static const noc_id_t FIR_FILTER_BLOCK = 0xF1120000;
+static const noc_id_t FOSPHOR_BLOCK = 0x666F0000;
static const noc_id_t SPLIT_STREAM_BLOCK = 0x57570000;
-static const noc_id_t RADIO_BLOCK = 0x12AD1000;
-static const noc_id_t VECTOR_IIR_BLOCK = 0x11120000;
+static const noc_id_t RADIO_BLOCK = 0x12AD1000;
+static const noc_id_t VECTOR_IIR_BLOCK = 0x11120000;
}} // namespace uhd::rfnoc
diff --git a/host/include/uhd/rfnoc/fft_block_control.hpp b/host/include/uhd/rfnoc/fft_block_control.hpp
index d4278b840..38ad70561 100644
--- a/host/include/uhd/rfnoc/fft_block_control.hpp
+++ b/host/include/uhd/rfnoc/fft_block_control.hpp
@@ -16,33 +16,136 @@ enum class fft_direction { REVERSE, FORWARD };
enum class fft_magnitude { COMPLEX, MAGNITUDE, MAGNITUDE_SQUARED };
// Custom property keys
-static const std::string PROP_KEY_MAGNITUDE = "magnitude";
-static const std::string PROP_KEY_DIRECTION = "direction";
-static const std::string PROP_KEY_FFT_LEN = "fft_len";
-static const std::string PROP_KEY_FFT_SCALING = "fft_scaling";
-static const std::string PROP_KEY_FFT_SHIFT = "fft_shift";
+static const std::string PROP_KEY_MAGNITUDE = "magnitude";
+static const std::string PROP_KEY_DIRECTION = "direction";
+static const std::string PROP_KEY_LENGTH = "length";
+static const std::string PROP_KEY_FFT_SCALING = "fft_scaling";
+static const std::string PROP_KEY_SHIFT_CONFIG = "shift_config";
/*! FFT Block Control Class
+ *
+ * The FFT block is an RFNoC block that accepts signed complex 16-bit data
+ * at its input and computes the forward or reverse FFT of the input data,
+ * outputting signed complex 16-bit data at its output. The output data
+ * may be configured as complex, magnitude, or mag-squared values, its
+ * spectrum shifted and/or reversed, and scaled by a scaled factor.
+ *
+ * The FFT length is configured via the length parameter, up to a maximum
+ * of 2048 samples. The FFT IP requires a power-of-two number of samples;
+ * the length will be coerced to the closest power of two which is smaller
+ * than length. The block will output packets of the same length in the
+ * desired format as configured via the API.
*/
class UHD_API fft_block_control : public noc_block_base
{
public:
RFNOC_DECLARE_BLOCK(fft_block_control)
- // Readback addresses
- static const uint32_t RB_FFT_RESET;
- static const uint32_t RB_MAGNITUDE_OUT;
- static const uint32_t RB_FFT_SIZE_LOG2;
- static const uint32_t RB_FFT_DIRECTION;
- static const uint32_t RB_FFT_SCALING;
- static const uint32_t RB_FFT_SHIFT_CONFIG;
- // Write addresses
- static const uint32_t SR_FFT_RESET;
- static const uint32_t SR_FFT_SIZE_LOG2;
- static const uint32_t SR_MAGNITUDE_OUT;
- static const uint32_t SR_FFT_DIRECTION;
- static const uint32_t SR_FFT_SCALING;
- static const uint32_t SR_FFT_SHIFT_CONFIG;
+ static const uint32_t REG_RESET_ADDR;
+ static const uint32_t REG_LENGTH_LOG2_ADDR;
+ static const uint32_t REG_MAGNITUDE_OUT_ADDR;
+ static const uint32_t REG_DIRECTION_ADDR;
+ static const uint32_t REG_SCALING_ADDR;
+ static const uint32_t REG_SHIFT_CONFIG_ADDR;
+
+ /*! Set the FFT direction
+ *
+ * Sets the direction of the FFT, either forward (FORWARD) or inverse
+ * (REVERSE).
+ *
+ * \param direction FFT direction
+ */
+ virtual void set_direction(const fft_direction direction) = 0;
+
+ /*! Get the FFT direction
+ *
+ * Returns the current direction of the FFT.
+ *
+ * \returns FFT direction
+ */
+ virtual fft_direction get_direction() const = 0;
+
+ /*! Set the format of the returned FFT output data
+ *
+ * Sets the format in which the FFT output data is returned. The following
+ * formats are supported:
+ *
+ * * amplitude/phase data (COMPLEX)
+ * * magnitude data (MAGNITUDE)
+ * * mag-squared data (MAGNITUDE_SQUARED)
+ *
+ * \param magnitude Format of the returned FFT output data
+ */
+ virtual void set_magnitude(const fft_magnitude magnitude) = 0;
+
+ /*! Get the format of the returned FFT output data
+ *
+ * Returns the current output format of the FFT data.
+ *
+ * \returns Format of the returned FFT output data
+ */
+ virtual fft_magnitude get_magnitude() const = 0;
+
+ /*! Set the shift configuration of the output FFT data
+ *
+ * Sets how the FFT output data is shifted (to get the zero frequency bin
+ * to the center of the output data). The following output data shift
+ * configurations are supported:
+ *
+ * * Negative frequencies first, then positive frequencies (NORMAL)
+ * * Positive frequencies first, then negative frequencies (REVERSE)
+ * * Bypass the shift altogether, leaving the zero frequency bin
+ * returned first (NATURAL).
+ *
+ * \param shift Configuration for shifting FFT output data
+ */
+ virtual void set_shift_config(const fft_shift shift) = 0;
+
+ /*! Get the shift configuration of the output FFT data
+ *
+ * Returns the current shift configuration of the output FFT data.
+ *
+ * \returns Shift configuration of the output FFT data
+ */
+ virtual fft_shift get_shift_config() const = 0;
+
+ /*! Set the scaling schedule for the FFT block
+ *
+ * Sets the scaling for each stage of the FFT. This value maps directly
+ * to the scale schedule field in the configuration channel data that is
+ * passed to the Xilinx AXI FFT IP. For more information on the format
+ * of this data, see Xilinx document PG109, Fast Fourier Transform
+ * LogiCORE IP Product Guide.
+ *
+ * \param scaling Scaling schedule for the FFT block
+ */
+ virtual void set_scaling(const uint16_t scaling) = 0;
+
+ /*! Get the scaling schedule for the FFT block
+ *
+ * Returns the current scaling schedule for the FFT block.
+ *
+ * \returns Scaling schedule for the FFT block
+ */
+ virtual uint16_t get_scaling() const = 0;
+
+ /*! Set the length of the FFT
+ *
+ * Sets the length of the FFT in number of samples. Note that the FFT
+ * IP requires a power-of-two number of samples; the incoming value will
+ * be coerced to the closest smaller power of two.
+ *
+ * \param length Desired FFT length
+ */
+ virtual void set_length(const size_t length) = 0;
+
+ /*! Get the length of the FFT
+ *
+ * Returns the current length of the FFT.
+ *
+ * \returns Current FFT length
+ */
+ virtual size_t get_length() const = 0;
};
}} // namespace uhd::rfnoc
diff --git a/host/lib/rfnoc/fft_block_control.cpp b/host/lib/rfnoc/fft_block_control.cpp
index c002d49bb..867d458ca 100644
--- a/host/lib/rfnoc/fft_block_control.cpp
+++ b/host/lib/rfnoc/fft_block_control.cpp
@@ -14,30 +14,27 @@ using namespace uhd::rfnoc;
namespace {
-constexpr int DEFAULT_SIZE = 256;
-const fft_shift DEFAULT_SHIFT = fft_shift::NORMAL;
-const fft_direction DEFAULT_DIRECTION = fft_direction::FORWARD;
-const fft_magnitude DEFAULT_MAGNITUDE = fft_magnitude::COMPLEX;
-constexpr int DEFAULT_FFT_SCALING = 1706;
+constexpr int DEFAULT_LENGTH = 256;
+constexpr fft_shift DEFAULT_SHIFT = fft_shift::NORMAL;
+constexpr fft_direction DEFAULT_DIRECTION = fft_direction::FORWARD;
+constexpr fft_magnitude DEFAULT_MAGNITUDE = fft_magnitude::COMPLEX;
+constexpr int DEFAULT_FFT_SCALING = 1706; // Conservative 1/N scaling
+
+// FFT IP constraints
+constexpr int MIN_FFT_LENGTH = 8;
+constexpr int MAX_FFT_LENGTH = 2048;
const uhd::rfnoc::io_type_t DEFAULT_TYPE = uhd::rfnoc::IO_TYPE_SC16;
} // namespace
-const uint32_t fft_block_control::RB_FFT_RESET = 0;
-const uint32_t fft_block_control::RB_MAGNITUDE_OUT = 8;
-const uint32_t fft_block_control::RB_FFT_SIZE_LOG2 = 16;
-const uint32_t fft_block_control::RB_FFT_DIRECTION = 24;
-const uint32_t fft_block_control::RB_FFT_SCALING = 32;
-const uint32_t fft_block_control::RB_FFT_SHIFT_CONFIG = 40;
-
-const uint32_t fft_block_control::SR_FFT_RESET = 131 * 8;
-const uint32_t fft_block_control::SR_FFT_SIZE_LOG2 = 132 * 8;
-const uint32_t fft_block_control::SR_MAGNITUDE_OUT = 133 * 8;
-const uint32_t fft_block_control::SR_FFT_DIRECTION = 134 * 8;
-const uint32_t fft_block_control::SR_FFT_SCALING = 135 * 8;
-const uint32_t fft_block_control::SR_FFT_SHIFT_CONFIG = 136 * 8;
+const uint32_t fft_block_control::REG_RESET_ADDR = 131 * 8;
+const uint32_t fft_block_control::REG_LENGTH_LOG2_ADDR = 132 * 8;
+const uint32_t fft_block_control::REG_MAGNITUDE_OUT_ADDR = 133 * 8;
+const uint32_t fft_block_control::REG_DIRECTION_ADDR = 134 * 8;
+const uint32_t fft_block_control::REG_SCALING_ADDR = 135 * 8;
+const uint32_t fft_block_control::REG_SHIFT_CONFIG_ADDR = 136 * 8;
class fft_block_control_impl : public fft_block_control
{
@@ -46,36 +43,129 @@ public:
{
set_prop_forwarding_policy(forwarding_policy_t::ONE_TO_ONE);
set_action_forwarding_policy(forwarding_policy_t::ONE_TO_ONE);
+ _reset();
+ _register_props();
}
- void reset()
+ void set_direction(const fft_direction direction)
{
- regs().poke32(SR_FFT_RESET, uint32_t(1));
- regs().poke32(SR_FFT_RESET, uint32_t(0));
+ set_property<int>(PROP_KEY_DIRECTION, static_cast<int>(direction));
+ }
+
+ fft_direction get_direction() const
+ {
+ return static_cast<fft_direction>(_direction.get());
+ }
+
+ void set_magnitude(const fft_magnitude magnitude)
+ {
+ set_property<int>(PROP_KEY_MAGNITUDE, static_cast<int>(magnitude));
+ }
+
+ fft_magnitude get_magnitude() const
+ {
+ return static_cast<fft_magnitude>(_magnitude.get());
+ }
+
+ void set_shift_config(const fft_shift shift)
+ {
+ set_property<int>(PROP_KEY_SHIFT_CONFIG, static_cast<int>(shift));
+ }
+
+ fft_shift get_shift_config() const
+ {
+ return static_cast<fft_shift>(_shift.get());
+ }
+
+ void set_scaling(const uint16_t scaling)
+ {
+ set_property<int>(PROP_KEY_FFT_SCALING, scaling);
+ }
+
+ uint16_t get_scaling() const
+ {
+ return static_cast<uint16_t>(_scaling.get());
+ }
+
+ void set_length(const size_t size)
+ {
+ set_property<int>(PROP_KEY_LENGTH, size);
+ }
+
+ size_t get_length() const
+ {
+ return static_cast<size_t>(_length.get());
}
private:
+ void _reset()
+ {
+ regs().poke32(REG_RESET_ADDR, uint32_t(1));
+ regs().poke32(REG_RESET_ADDR, uint32_t(0));
+ }
+
/**************************************************************************
* Initialization
*************************************************************************/
void _register_props()
{
// register block specific properties
- register_property(&_size, [this]() {
- this->regs().poke32(SR_FFT_SIZE_LOG2, uint32_t(this->_size.get()));
+ register_property(&_length);
+ add_property_resolver({&_length}, {&_length}, [this]() {
+ size_t length = this->_length.get();
+ if (length < MIN_FFT_LENGTH || length > MAX_FFT_LENGTH) {
+ throw uhd::value_error("Size value must be in ["
+ + std::to_string(MIN_FFT_LENGTH) + ", "
+ + std::to_string(MAX_FFT_LENGTH) + "]");
+ }
+ // Find the log2(length) via highest bit set
+ size_t length_log2 = 0;
+ size_t old_length = length;
+ while ((length >>= 1) != 0) {
+ length_log2++;
+ }
+ size_t coerced_length = (1 << length_log2);
+ if (old_length != coerced_length) {
+ RFNOC_LOG_WARNING("Length "
+ << old_length
+ << " not an integral power of two; coercing to "
+ << coerced_length);
+ this->_length.set(coerced_length);
+ }
+ this->regs().poke32(REG_LENGTH_LOG2_ADDR, uint32_t(length_log2));
});
+
register_property(&_magnitude, [this]() {
- this->regs().poke32(SR_MAGNITUDE_OUT, uint32_t(this->_magnitude.get()));
+ int mag = this->_magnitude.get();
+ if (mag < static_cast<int>(fft_magnitude::COMPLEX)
+ || mag > static_cast<int>(fft_magnitude::MAGNITUDE_SQUARED)) {
+ throw uhd::value_error("Magnitude value must be [0, 2]");
+ }
+ this->regs().poke32(REG_MAGNITUDE_OUT_ADDR, uint32_t(mag));
});
register_property(&_direction, [this]() {
- this->regs().poke32(SR_MAGNITUDE_OUT, uint32_t(this->_direction.get()));
+ int dir = _direction.get();
+ if (dir < static_cast<int>(fft_direction::REVERSE)
+ || dir > static_cast<int>(fft_direction::FORWARD)) {
+ throw uhd::value_error("Direction value must be in [0, 1]");
+ }
+ this->regs().poke32(REG_DIRECTION_ADDR, uint32_t(dir));
});
register_property(&_scaling, [this]() {
- this->regs().poke32(SR_MAGNITUDE_OUT, uint32_t(this->_scaling.get()));
+ int scale = _scaling.get();
+ if (scale < 0 || scale > (1 << 12) - 1) {
+ throw uhd::value_error("Scale value must be in [0, 4095]");
+ }
+ this->regs().poke32(REG_SCALING_ADDR, uint32_t(scale));
});
register_property(&_shift, [this]() {
- this->regs().poke32(SR_MAGNITUDE_OUT, uint32_t(this->_shift.get()));
+ int shift = this->_shift.get();
+ if (shift < static_cast<int>(fft_shift::NORMAL)
+ || shift > static_cast<int>(fft_shift::NATURAL)) {
+ throw uhd::value_error("Shift value must be [0, 2]");
+ }
+ this->regs().poke32(REG_SHIFT_CONFIG_ADDR, uint32_t(shift));
});
// register edge properties
@@ -91,7 +181,7 @@ private:
});
}
- property_t<int> _size{PROP_KEY_FFT_LEN, DEFAULT_SIZE, {res_source_info::USER}};
+ property_t<int> _length{PROP_KEY_LENGTH, DEFAULT_LENGTH, {res_source_info::USER}};
property_t<int> _magnitude = property_t<int>{
PROP_KEY_MAGNITUDE, static_cast<int>(DEFAULT_MAGNITUDE), {res_source_info::USER}};
property_t<int> _direction = property_t<int>{
@@ -99,7 +189,7 @@ private:
property_t<int> _scaling = property_t<int>{
PROP_KEY_FFT_SCALING, DEFAULT_FFT_SCALING, {res_source_info::USER}};
property_t<int> _shift = property_t<int>{
- PROP_KEY_FFT_SHIFT, static_cast<int>(DEFAULT_SHIFT), {res_source_info::USER}};
+ PROP_KEY_SHIFT_CONFIG, static_cast<int>(DEFAULT_SHIFT), {res_source_info::USER}};
property_t<std::string> _type_in = property_t<std::string>{
PROP_KEY_TYPE, IO_TYPE_SC16, {res_source_info::INPUT_EDGE}};
@@ -108,4 +198,4 @@ private:
};
UHD_RFNOC_BLOCK_REGISTER_DIRECT(
- fft_block_control, 0xFF700000, "FFT", CLOCK_KEY_GRAPH, "bus_clk")
+ fft_block_control, FFT_BLOCK, "FFT", CLOCK_KEY_GRAPH, "bus_clk")