diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/rfnoc/defaults.hpp | 15 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/fft_block_control.hpp | 141 | ||||
| -rw-r--r-- | host/lib/rfnoc/fft_block_control.cpp | 150 | 
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") | 
