diff options
| -rw-r--r-- | host/include/uhd/rfnoc/fir_block_ctrl.hpp | 46 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/null_block_ctrl.hpp | 70 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/window_block_ctrl.hpp | 54 | ||||
| -rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | host/lib/rfnoc/fir_block_ctrl_impl.cpp | 77 | ||||
| -rw-r--r-- | host/lib/rfnoc/null_block_ctrl_impl.cpp | 107 | ||||
| -rw-r--r-- | host/lib/rfnoc/window_block_ctrl_impl.cpp | 94 | 
7 files changed, 452 insertions, 0 deletions
diff --git a/host/include/uhd/rfnoc/fir_block_ctrl.hpp b/host/include/uhd/rfnoc/fir_block_ctrl.hpp new file mode 100644 index 000000000..bfed5e067 --- /dev/null +++ b/host/include/uhd/rfnoc/fir_block_ctrl.hpp @@ -0,0 +1,46 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP +#define INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> + +namespace uhd { +    namespace rfnoc { + +/*! \brief Block controller for the standard FIR RFNoC block. + * + * The standard FIR has the following features: + * - One input- and output-port + * - Configurable taps, but fixed number of taps + * - Supports data type sc16 (16-Bit fix-point complex samples) + * + * This block requires packets to be the same size as the FFT length. + * It will perform one FFT operation per incoming packet, treating it + * as a vector of samples. + */ +class UHD_RFNOC_API fir_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base +{ +public: +    UHD_RFNOC_BLOCK_OBJECT(fir_block_ctrl) + +    //! Configure the filter taps. +    // +    // The length of \p taps must correspond the number of taps +    // in this block. If it's shorter, zeros will be padded. +    // If it's longer, throws a uhd::value_error. +    virtual void set_taps(const std::vector<int> &taps) = 0; + +    //! Returns the number of filter taps in this block. +    virtual size_t get_n_taps() const = 0; +}; /* class fir_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_fir_block_ctrl_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/null_block_ctrl.hpp b/host/include/uhd/rfnoc/null_block_ctrl.hpp new file mode 100644 index 000000000..8a982b3e0 --- /dev/null +++ b/host/include/uhd/rfnoc/null_block_ctrl.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> + +namespace uhd { +    namespace rfnoc { + +/*! \brief Provide access to a 'null block'. + * + * A 'null block' is a specific block, which comes with a couple + * of features useful for testing: + * - It can produce data at a given line rate, with a configurable + *   packet size. + * - It can be used to dump packets ("null sink", "bit bucket") + * + * This block also serves as an example of how to create your own + * C++ classes to control your block. + * + * As a true source, it understands the following stream commands: + * - STREAM_MODE_START_CONTINUOUS + * - STREAM_MODE_STOP_CONTINUOUS + * + * Other stream commands are not understood and issue_stream_cmd() + * will throw if it receives them. + */ +class null_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base +{ +public: +    // This macro must always be at the top of the public section in an RFNoC block class +    UHD_RFNOC_BLOCK_OBJECT(null_block_ctrl) + +    //! Set this register to number of lines per packet +    static const uint32_t SR_LINES_PER_PACKET = 129; +    //! Set this register to number of cycles between producing a line +    static const uint32_t SR_LINE_RATE = 130; +    //! Set this register to non-zero to start producing data +    static const uint32_t SR_ENABLE_STREAM = 131; + +    static const size_t DEFAULT_LINES_PER_PACKET = 32; +    static const size_t BYTES_PER_LINE = 8; + +    //! Custom function to set the rate at which data is produced. +    // Note: This is 'cycles per line', so the bit rate is actually +    // 64 times this value (byte/s is 8*rate etc.) +    // +    // Equivalent to writing to line_rate/value in the property tree. +    // +    // \param The rate you want to set this to +    // \param The clock rate of this block's clock domain +    // \returns the actual line rate (will find closest possible). +    virtual double set_line_rate(double rate, double clock_rate=166.6e6) = 0; + +    //! Return the current line rate. Equivalent to reading line_rate/value +    // from the property tree. +    virtual double get_line_rate(double clock_rate=166.6e6) const = 0; + +}; /* class null_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_NULL_BLOCK_CTRL_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/window_block_ctrl.hpp b/host/include/uhd/rfnoc/window_block_ctrl.hpp new file mode 100644 index 000000000..093b34e67 --- /dev/null +++ b/host/include/uhd/rfnoc/window_block_ctrl.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> + +namespace uhd { +    namespace rfnoc { + +/*! \brief Block controller for the standard windowing RFNoC block. + * + * The standard windowing block has the following features: + * - One input- and output-port + * - Configurable window length and coefficients + * - Supports data type sc16 (16-Bit fix-point complex samples) + * + * This block requires packets to be the same size as the downstream FFT length. + * It will perform one window operation per incoming packet, treating it + * as a vector of samples. + */ +class UHD_RFNOC_API window_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base +{ +public: +    UHD_RFNOC_BLOCK_OBJECT(window_block_ctrl) + +    static const size_t          MAX_COEFF_VAL          = 32767; +    static const uint32_t SR_WINDOW_LEN          = 131;               // Note: AXI config bus uses 129 & 130 +    static const uint32_t RB_MAX_WINDOW_LEN      = 0; +    static const uint32_t AXIS_WINDOW_LOAD       = AXIS_CONFIG_BUS+0; // 2*0+0 +    static const uint32_t AXIS_WINDOW_LOAD_TLAST = AXIS_CONFIG_BUS+1; // 2*0+1 + +    //! Configure the window coefficients +    // +    // \p coeffs size determines the window length. If it longer than +    // the maximum window length, throws a uhd::value_error. +    virtual void set_window(const std::vector<int> &coeffs) = 0; + +    //! Returns the maximum window length. +    virtual size_t get_max_len() const = 0; + +    //! Returns the current window length. +    virtual size_t get_window_len() const = 0; + +}; /* class window_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_WINDOW_BLOCK_CTRL_HPP */ diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index 9ee4970a3..527c99e14 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -34,7 +34,11 @@ LIBUHD_APPEND_SOURCES(      # Default block control classes:      ${CMAKE_CURRENT_SOURCE_DIR}/ddc_block_ctrl_impl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/duc_block_ctrl_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/fir_block_ctrl_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/null_block_ctrl_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/window_block_ctrl_impl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/siggen_block_ctrl_impl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_block_ctrl_impl.cpp  ) diff --git a/host/lib/rfnoc/fir_block_ctrl_impl.cpp b/host/lib/rfnoc/fir_block_ctrl_impl.cpp new file mode 100644 index 000000000..4267e0b22 --- /dev/null +++ b/host/lib/rfnoc/fir_block_ctrl_impl.cpp @@ -0,0 +1,77 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/fir_block_ctrl.hpp> +#include <uhd/convert.hpp> +#include <uhd/utils/log.hpp> + +using namespace uhd::rfnoc; + +class fir_block_ctrl_impl : public fir_block_ctrl +{ +public: +    static const uint32_t RB_NUM_TAPS     = 0; +    static const uint32_t SR_RELOAD       = 128; +    static const uint32_t SR_RELOAD_TLAST = 129; +    static const uint32_t SR_CONFIG       = 130; + +    UHD_RFNOC_BLOCK_CONSTRUCTOR(fir_block_ctrl), +        _item_type("sc16") // We only support sc16 in this block +    { +        _n_taps = uint32_t(user_reg_read64(RB_NUM_TAPS)); +        UHD_LOGGER_DEBUG(unique_id()) +            << "fir_block::fir_block() n_taps ==" << _n_taps << std::endl; +        UHD_ASSERT_THROW(_n_taps); + +        // Default to Dirac impulse +        std::vector<int> default_taps(1, 20000); +        set_taps(default_taps); +    } + +    void set_taps(const std::vector<int> &taps_) +    { +        UHD_LOGGER_TRACE(unique_id()) << "fir_block::set_taps()" << std::endl; +        if (taps_.size() > _n_taps) { +            throw uhd::value_error(str( +                boost::format("FIR block: Too many filter coefficients! Provided %d, FIR allows %d.\n") +                % taps_.size() % _n_taps +            )); +        } +        for (size_t i = 0; i < taps_.size(); i++) { +            if (taps_[i] > 32767 || taps_[i] < -32768) { +                throw uhd::value_error(str( +                    boost::format("FIR block: Coefficient %d out of range! Value %d, Allowed range [-32768,32767].\n") +                    % i % taps_[i])); +            } +        } +        std::vector<int> taps = taps_; +        if (taps.size() < _n_taps) { +            taps.resize(_n_taps, 0); +        } + +        // Write taps via the reload bus +        for (size_t i = 0; i < taps.size() - 1; i++) { +            sr_write(SR_RELOAD, uint32_t(taps[i])); +        } +        // Assert tlast when sending the spinal tap (haha, it's actually the final tap). +        sr_write(SR_RELOAD_TLAST, uint32_t(taps.back())); +        // Send the configuration word to replace the existing coefficients with the new ones. +        // Note: This configuration bus does not require tlast +        sr_write(SR_CONFIG, 0); +    } + +    //! Returns the number of filter taps in this block. +    size_t get_n_taps() const +    { +        return _n_taps; +    } + +private: +    const std::string _item_type; +    size_t _n_taps; +}; + +UHD_RFNOC_BLOCK_REGISTER(fir_block_ctrl, "FIR"); diff --git a/host/lib/rfnoc/null_block_ctrl_impl.cpp b/host/lib/rfnoc/null_block_ctrl_impl.cpp new file mode 100644 index 000000000..7e62a2b3e --- /dev/null +++ b/host/lib/rfnoc/null_block_ctrl_impl.cpp @@ -0,0 +1,107 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/utils/log.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/rfnoc/null_block_ctrl.hpp> +#include <boost/format.hpp> + +using namespace uhd::rfnoc; + +class null_block_ctrl_impl : public null_block_ctrl +{ +public: +    UHD_RFNOC_BLOCK_CONSTRUCTOR(null_block_ctrl) +    { +        // Register hooks for line_rate: +        _tree->access<int>(_root_path / "args" / 0 /  "line_rate" / "value") +            .add_coerced_subscriber([this](const int delay){ +                this->set_line_delay_cycles(delay); +            }) +            .update() +        ; +        // Register hooks for bpp: +        _tree->access<int>(_root_path / "args" / 0 /  "bpp" / "value") +            .add_coerced_subscriber([this](const int bpp){ +                this->set_bytes_per_packet(bpp); +            }) +            .update() +        ; +    } + +    void set_line_delay_cycles(int cycles) +    { +        sr_write(SR_LINE_RATE, uint32_t(cycles)); +    } + +    void set_bytes_per_packet(int bpp) +    { +        sr_write(SR_LINES_PER_PACKET, uint32_t(bpp / BYTES_PER_LINE)); +    } + +    double set_line_rate(double rate, double clock_rate) +    { +        int cycs_between_lines = clock_rate / rate - 1; +        if (cycs_between_lines > 0xFFFF) { +            cycs_between_lines = 0xFFFF; +            UHD_LOGGER_WARNING(unique_id()) +                << str(boost::format("Requested rate %f is larger than possible " +                                     "with the current clock rate (%.2f MHz).") +                       % rate % (clock_rate / 1e6)) +                << std::endl; +        } +        cycs_between_lines = std::max(0, cycs_between_lines); +        set_arg<int>("line_rate", cycs_between_lines); +        return _line_rate_from_reg_val(cycs_between_lines, clock_rate); +    } + +    double get_line_rate(double clock_rate) const +    { +        return _line_rate_from_reg_val(get_arg<int>("line_rate"), clock_rate); +    } + +    double _line_rate_from_reg_val(uint32_t reg_val, double clock_rate) const +    { +        return clock_rate / (reg_val + 1); +    } + +    void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t) +    { +        if (not stream_cmd.stream_now) { +            throw uhd::not_implemented_error("null_block does not support timed commands."); +        } +        switch (stream_cmd.stream_mode) { +            case uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: +                sr_write(SR_ENABLE_STREAM, true); +                break; + +            case uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS: +                sr_write(SR_ENABLE_STREAM, false); +                break; + +            case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE: +            case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE: +                throw uhd::not_implemented_error("null_block does not support streaming modes other than CONTINUOUS"); + +            default: +                UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    void set_destination( +            uint32_t next_address, +            size_t output_block_port +    ) { +        uhd::sid_t sid(next_address); +        if (sid.get_src() == 0) { +            sid.set_src(get_address()); +        } +        sr_write(SR_NEXT_DST_SID, sid.get(), output_block_port); +    } +}; + +UHD_RFNOC_BLOCK_REGISTER(null_block_ctrl, "NullSrcSink"); + diff --git a/host/lib/rfnoc/window_block_ctrl_impl.cpp b/host/lib/rfnoc/window_block_ctrl_impl.cpp new file mode 100644 index 000000000..74ebd146d --- /dev/null +++ b/host/lib/rfnoc/window_block_ctrl_impl.cpp @@ -0,0 +1,94 @@ +// +// Copyright 2014-2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/window_block_ctrl.hpp> +#include <uhd/convert.hpp> +#include <uhd/utils/log.hpp> + +using namespace uhd::rfnoc; + +class window_block_ctrl_impl : public window_block_ctrl +{ +public: +    UHD_RFNOC_BLOCK_CONSTRUCTOR(window_block_ctrl), +        _item_type("sc16"), // We only support sc16 in this block +        _bpi(uhd::convert::get_bytes_per_item("sc16")) +    { +        _max_len = uint32_t(user_reg_read64(RB_MAX_WINDOW_LEN)); +        UHD_LOGGER_DEBUG(unique_id()) +            << "window_block::window_block() max_len ==" << _max_len << std::endl; +        UHD_ASSERT_THROW(_max_len); + +        // TODO we need a coercer to check that spp on the prop tree doesn't get set to anything invalid +        _set_default_window(std::min<size_t>(get_arg<int>("spp"), _max_len)); +    } + +    //! Set window coefficients and length +    void set_window(const std::vector<int> &coeffs) +    { +        UHD_LOGGER_TRACE(unique_id()) +            << "window_block::set_window()" << std::endl; +        if (coeffs.size() > _max_len) { +            throw uhd::value_error(str( +                boost::format("window_block::set_window(): Too many window " +                              "coefficients! Provided %d, window allows up to %d.\n") +                % coeffs.size() % _max_len +            )); +        } + +        size_t window_len = coeffs.size(); + +        // Window block can take complex coefficients in sc16 format, but typical usage is +        // to have real(coeffs) == imag(coeffs) +        std::vector<uint32_t> coeffs_; +        for (size_t i = 0; i < window_len - 1; i++) { +            if (coeffs[i] > 32767 || coeffs[i] < -32768) { +                throw uhd::value_error(str( +                    boost::format("window_block::set_window(): Coefficient %d " +                                  "(index %d) outside coefficient range [-32768,32767].\n") +                    % coeffs[i] % i)); +            } +            coeffs_.push_back(coeffs[i]); +        } + +        // Write coefficients via the load bus +        for (size_t i = 0; i < window_len - 1; i++) { +            sr_write(AXIS_WINDOW_LOAD, coeffs_[i]); +        } +        // Assert tlast when sending the final coefficient (sorry, no joke here) +        sr_write(AXIS_WINDOW_LOAD_TLAST, coeffs_.back()); +        // Set the window length +        sr_write(SR_WINDOW_LEN, window_len); + +        // This block requires spp to match the window length: +        set_arg<int>("spp", int(window_len)); +    } + +    //! Returns the maximum window length of this block. +    size_t get_max_len() const +    { +        return _max_len; +    } + +    size_t get_window_len() const +    { +        return size_t(get_arg<int>("spp")); +    } + + +private: +    const std::string _item_type; +    const size_t _bpi; +    size_t _max_len; + +    //! Default is a rectangular window +    void _set_default_window(size_t window_len) { +        std::vector<int> default_coeffs(window_len, (1 << 15)-1); +        set_window(default_coeffs); +    } +}; + +UHD_RFNOC_BLOCK_REGISTER(window_block_ctrl, "Window");  | 
