diff options
| -rw-r--r-- | host/include/uhd/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/null_block_control.hpp | 88 | ||||
| -rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/null_block_control.cpp | 182 | ||||
| -rw-r--r-- | host/tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/tests/rfnoc_blocks_test.cpp | 87 | 
6 files changed, 360 insertions, 0 deletions
| diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt index 6251a555d..f5cb4006b 100644 --- a/host/include/uhd/rfnoc/CMakeLists.txt +++ b/host/include/uhd/rfnoc/CMakeLists.txt @@ -41,6 +41,7 @@ if(ENABLE_RFNOC)          duc_block_ctrl.hpp          fir_block_ctrl.hpp          null_block_ctrl.hpp +        null_block_control.hpp          radio_ctrl.hpp          radio_control.hpp          replay_block_ctrl.hpp diff --git a/host/include/uhd/rfnoc/null_block_control.hpp b/host/include/uhd/rfnoc/null_block_control.hpp new file mode 100644 index 000000000..74f6db2a3 --- /dev/null +++ b/host/include/uhd/rfnoc/null_block_control.hpp @@ -0,0 +1,88 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP +#define INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP + +#include <uhd/config.hpp> +#include <uhd/rfnoc/noc_block_base.hpp> +#include <uhd/types/stream_cmd.hpp> + +namespace uhd { namespace rfnoc { + +class UHD_API null_block_control : public noc_block_base +{ +public: +    RFNOC_DECLARE_BLOCK(null_block_control) + +    enum port_type_t { SINK, SOURCE, LOOP }; +    enum count_type_t { LINES, PACKETS }; + +    static const uint32_t REG_CTRL_STATUS; +    static const uint32_t REG_SRC_LINES_PER_PKT; +    static const uint32_t REG_SRC_BYTES_PER_PKT; +    static const uint32_t REG_SRC_THROTTLE_CYC; +    static const uint32_t REG_SNK_LINE_CNT_LO; +    static const uint32_t REG_SNK_LINE_CNT_HI; +    static const uint32_t REG_SNK_PKT_CNT_LO; +    static const uint32_t REG_SNK_PKT_CNT_HI; +    static const uint32_t REG_SRC_LINE_CNT_LO; +    static const uint32_t REG_SRC_LINE_CNT_HI; +    static const uint32_t REG_SRC_PKT_CNT_LO; +    static const uint32_t REG_SRC_PKT_CNT_HI; +    static const uint32_t REG_LOOP_LINE_CNT_LO; +    static const uint32_t REG_LOOP_LINE_CNT_HI; +    static const uint32_t REG_LOOP_PKT_CNT_LO; +    static const uint32_t REG_LOOP_PKT_CNT_HI; + +    /*! Start/stop the null source (port 0) +     */ +    virtual void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd) = 0; + +    /*! Reset the counters +     */ +    virtual void reset_counters() = 0; + +    /*! Set bytes per packet +     * +     * Note: This sets the entire packet size, including header. +     */ +    virtual void set_bytes_per_packet(const uint32_t bpp) = 0; + +    /*! Set throttle cycles +     * +     * The block will wait this many cycles between packets. Useful for reducing +     * the data output. +     */ +    virtual void set_throttle_cycles(const uint32_t cycs) = 0; + +    /*! Get lines per packet (including the header!) +     */ +    virtual uint32_t get_lines_per_packet() = 0; + +    /*! Get bytes per packet +     */ +    virtual uint32_t get_bytes_per_packet() = 0; + +    /*! Get throttle cycles +     */ +    virtual uint32_t get_throttle_cycles() = 0; + +    /*! Returns the number of lines that have been consumed on port 0 since the +     * last reset +     * +     * \param port_type The port for which the counter value should be returned, +     *        sink, source, or loop. +     * \param count_type If we want the packet count, or the line count +     */ +    virtual uint64_t get_count( +        const port_type_t port_type, const count_type_t count_type) = 0; +}; + +}} // namespace uhd::rfnoc + +#endif /* INCLUDED_LIBUHD_NULL_BLOCK_CONTROL_HPP */ + diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index d62489484..1b627d304 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -57,6 +57,7 @@ LIBUHD_APPEND_SOURCES(      ${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}/null_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/window_block_ctrl_impl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_impl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp diff --git a/host/lib/rfnoc/null_block_control.cpp b/host/lib/rfnoc/null_block_control.cpp new file mode 100644 index 000000000..be0738b76 --- /dev/null +++ b/host/lib/rfnoc/null_block_control.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/null_block_control.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <uhd/rfnoc/defaults.hpp> +#include <atomic> + +namespace { + +} // namespace + +using namespace uhd::rfnoc; +using uhd::stream_cmd_t; + + +const uint32_t null_block_control::REG_CTRL_STATUS       = 0x00; +const uint32_t null_block_control::REG_SRC_LINES_PER_PKT = 0x04; +const uint32_t null_block_control::REG_SRC_BYTES_PER_PKT = 0x08; +const uint32_t null_block_control::REG_SRC_THROTTLE_CYC  = 0x0C; +const uint32_t null_block_control::REG_SNK_LINE_CNT_LO   = 0x10; +const uint32_t null_block_control::REG_SNK_LINE_CNT_HI   = 0x14; +const uint32_t null_block_control::REG_SNK_PKT_CNT_LO    = 0x18; +const uint32_t null_block_control::REG_SNK_PKT_CNT_HI    = 0x1C; +const uint32_t null_block_control::REG_SRC_LINE_CNT_LO   = 0x20; +const uint32_t null_block_control::REG_SRC_LINE_CNT_HI   = 0x24; +const uint32_t null_block_control::REG_SRC_PKT_CNT_LO    = 0x28; +const uint32_t null_block_control::REG_SRC_PKT_CNT_HI    = 0x2C; +const uint32_t null_block_control::REG_LOOP_LINE_CNT_LO  = 0x30; +const uint32_t null_block_control::REG_LOOP_LINE_CNT_HI  = 0x34; +const uint32_t null_block_control::REG_LOOP_PKT_CNT_LO   = 0x38; +const uint32_t null_block_control::REG_LOOP_PKT_CNT_HI   = 0x3C; + + +class null_block_control_impl : public null_block_control +{ +public: +    RFNOC_BLOCK_CONSTRUCTOR(null_block_control) +    { +        uint32_t initial_state = regs().peek32(REG_CTRL_STATUS); +        _streaming = initial_state & 0x2; +        _nipc = (initial_state >> 24) & 0xFF; +        _item_width = (initial_state >> 16) & 0xFF; +        reset_counters(); +        register_issue_stream_cmd(); +    } + +    void issue_stream_cmd(const stream_cmd_t& stream_cmd) +    { +        if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) { +            RFNOC_LOG_TRACE("Received start stream request!"); +            regs().poke32(REG_CTRL_STATUS, 0x2); +            _streaming = true; +        } else if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) { +            RFNOC_LOG_TRACE("Received stop stream request!"); +            regs().poke32(REG_CTRL_STATUS, 0x0); +            _streaming = false; +        } else { +            throw uhd::runtime_error("Null source can only do continuous streaming!"); +        } +    } + +    void reset_counters() +    { +        const uint32_t streaming_flag = _streaming ? 0x2 : 0x0; +        regs().poke32(REG_CTRL_STATUS, streaming_flag | 0x1); +        regs().poke32(REG_CTRL_STATUS, streaming_flag | 0x0); +    } + +    void set_lines_per_packet(const uint32_t lpp) +    { +        if (lpp < 2) { +            throw uhd::value_error("Null source lines per packet must be at " +                                   "least one line in the payload!"); +        } +        if (lpp > 0xFFF) { +            throw uhd::value_error("Null source lines per packet cannot exceed 12 bits!"); +        } +        // The register value is decreased by one line for the header, and one +        // line for encoding (if we write a 0 here, the payload will still be at +        // least one line long). +        regs().poke32(REG_SRC_LINES_PER_PKT, lpp - 2); +    } + +    void set_bytes_per_packet(const uint32_t bpp) +    { +        if (bpp > 0xFFFF) { +            throw uhd::value_error("Null source lines per packet cannot exceed 16 bits!"); +        } +        regs().poke32(REG_SRC_BYTES_PER_PKT, bpp); +        const uint32_t bytes_per_line = (_item_width * _nipc) / 8; +        // If bpp is not an integer multiple of bytes_per_line, then we add a +        // full additional line! +        const uint32_t lpp = bpp / bytes_per_line + (bpp % bytes_per_line ? 1 : 0); +        set_lines_per_packet(lpp); +    } + +    void set_throttle_cycles(const uint32_t cycs) +    { +        if (cycs > 0x3FF) { +            throw uhd::value_error("Null source throttle cycles cannot exceed 10 bits!"); +        } +        regs().poke32(REG_SRC_THROTTLE_CYC, cycs); +    } + +    uint32_t get_lines_per_packet() +    { +        return regs().peek32(REG_SRC_LINES_PER_PKT) + 2; +    } + +    uint32_t get_bytes_per_packet() +    { +        return regs().peek32(REG_SRC_BYTES_PER_PKT); +    } + +    uint32_t get_throttle_cycles() +    { +        return regs().peek32(REG_SRC_THROTTLE_CYC); +    } + +    uint64_t get_count( +        const port_type_t port_type, const count_type_t count_type) +    { +        const uint32_t count_addr_lo = [&](){ +            switch (port_type) { +                case SOURCE: +                    return count_type == LINES ? REG_SRC_LINE_CNT_LO : REG_SRC_PKT_CNT_LO; +                case SINK: +                    return count_type == LINES ? REG_SNK_LINE_CNT_LO : REG_SNK_PKT_CNT_LO; +                case LOOP: +                    return count_type == LINES ? REG_LOOP_LINE_CNT_LO : REG_LOOP_PKT_CNT_LO; +                default: +                    UHD_THROW_INVALID_CODE_PATH(); +            } +        }(); +        return regs().peek64(count_addr_lo); +    } + +private: + +    /*! Action API: Register a handler for stream commands +     */ +    void register_issue_stream_cmd() +    { +        register_action_handler(ACTION_KEY_STREAM_CMD, +            [this](const res_source_info& src, action_info::sptr action) { +                stream_cmd_action_info::sptr stream_cmd_action = +                    std::dynamic_pointer_cast<stream_cmd_action_info>(action); +                if (!stream_cmd_action) { +                    throw uhd::runtime_error( +                        "Received stream_cmd of invalid action type!"); +                } +                if (src.instance != 0 || src.type != res_source_info::OUTPUT_EDGE) { +                    throw uhd::runtime_error( +                        "The null source can only stream from output port 0!"); +                } +                RFNOC_LOG_DEBUG("Received stream command action request!"); +                issue_stream_cmd(stream_cmd_action->stream_cmd); +            }); +    } + +    /************************************************************************** +     * FPGA communication (register IO) +     *************************************************************************/ + +    /************************************************************************** +     * Attributes +     *************************************************************************/ +    //! True if the source port 0 is producing data +    std::atomic_bool _streaming{false}; + +    //! Number of items per clock +    uint32_t _nipc; + +    //! Bits per item +    uint32_t _item_width; +}; + +UHD_RFNOC_BLOCK_REGISTER_DIRECT(null_block_control, 0x00000001, "NullSrcSink") diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 484eeb903..df891b7e2 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -250,6 +250,7 @@ UHD_ADD_NONAPI_TEST(      ${CMAKE_SOURCE_DIR}/lib/rfnoc/block_id.cpp      ${CMAKE_SOURCE_DIR}/lib/utils/compat_check.cpp      ${CMAKE_SOURCE_DIR}/lib/rfnoc/ddc_block_control.cpp +    ${CMAKE_SOURCE_DIR}/lib/rfnoc/null_block_control.cpp      ${CMAKE_SOURCE_DIR}/lib/rfnoc/radio_control_impl.cpp      ${CMAKE_SOURCE_DIR}/lib/usrp/cores/dsp_core_utils.cpp  ) diff --git a/host/tests/rfnoc_blocks_test.cpp b/host/tests/rfnoc_blocks_test.cpp index 4b393eb36..1832fc6ae 100644 --- a/host/tests/rfnoc_blocks_test.cpp +++ b/host/tests/rfnoc_blocks_test.cpp @@ -5,10 +5,13 @@  //  #include "rfnoc_mock_reg_iface.hpp" +#include <uhd/rfnoc/null_block_control.hpp>  #include <uhd/rfnoc/ddc_block_control.hpp> +#include <uhd/rfnoc/actions.hpp>  #include <uhdlib/rfnoc/clock_iface.hpp>  #include <uhdlib/rfnoc/node_accessor.hpp>  #include <uhdlib/rfnoc/clock_iface.hpp> +#include <uhdlib/utils/narrow.hpp>  #include <boost/test/unit_test.hpp>  #include <iostream> @@ -38,8 +41,92 @@ noc_block_base::make_args_ptr make_make_args(noc_block_base::noc_id_t noc_id,      uhd::rfnoc::noc_block_base::sptr BLOCK_NAME##_make( \          uhd::rfnoc::noc_block_base::make_args_ptr make_args); +MOCK_REGISTER(null_block_control)  MOCK_REGISTER(ddc_block_control) +BOOST_AUTO_TEST_CASE(test_null_block) +{ +    node_accessor_t node_accessor{}; +    constexpr size_t num_chans                 = 2; +    constexpr uint32_t nipc                    = 2; +    constexpr uint32_t item_width              = 32; +    constexpr noc_block_base::noc_id_t mock_id = 0x7E570000; + +    auto make_args = make_make_args(mock_id, "0/NullSrcSink#0", num_chans, num_chans); +    auto reg_iface = std::dynamic_pointer_cast<mock_reg_iface_t>(make_args->reg_iface); +    auto set_mem   = [&](const uint32_t addr, const uint32_t data) { +        reg_iface->read_memory[addr] = data; +    }; +    auto get_mem = [&](const uint32_t addr) { return reg_iface->write_memory[addr]; }; +    auto copy_mem = [&](const uint32_t addr) { set_mem(addr, get_mem(addr)); }; + +    set_mem(null_block_control::REG_CTRL_STATUS, (nipc << 24) | (item_width << 16)); +    auto test_null = std::dynamic_pointer_cast<null_block_control>( +        null_block_control_make(std::move(make_args))); + +    using uhd::stream_cmd_t; +    node_accessor.init_props(test_null.get()); +    uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +    test_null->issue_stream_cmd(stream_cmd); +    stream_cmd.stream_mode = stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE; +    BOOST_REQUIRE_THROW(test_null->issue_stream_cmd(stream_cmd), uhd::runtime_error); + +    constexpr uint64_t snk_count = 1000000000; +    constexpr uint64_t snk_count_pkts = 5; +    constexpr uint64_t src_count = 2323232323; +    constexpr uint64_t loop_count = 4242424242; +    set_mem(null_block_control::REG_SNK_LINE_CNT_LO, +        uhd::narrow_cast<uint32_t>(snk_count & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_SNK_LINE_CNT_HI, +        uhd::narrow_cast<uint32_t>((snk_count >> 32) & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_SNK_PKT_CNT_LO, +        uhd::narrow_cast<uint32_t>(snk_count_pkts & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_SNK_PKT_CNT_HI, +        uhd::narrow_cast<uint32_t>((snk_count_pkts >> 32) & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_SRC_LINE_CNT_LO, +        uhd::narrow_cast<uint32_t>(src_count & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_SRC_LINE_CNT_HI, +        uhd::narrow_cast<uint32_t>((src_count >> 32) & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_LOOP_LINE_CNT_LO, +        uhd::narrow_cast<uint32_t>(loop_count & 0xFFFFFFFF)); +    set_mem(null_block_control::REG_LOOP_LINE_CNT_HI, +        uhd::narrow_cast<uint32_t>((loop_count >> 32) & 0xFFFFFFFF)); +    BOOST_CHECK_EQUAL( +        test_null->get_count(null_block_control::SINK, null_block_control::LINES), +        snk_count); +    BOOST_CHECK_EQUAL( +        test_null->get_count(null_block_control::SINK, null_block_control::PACKETS), +        snk_count_pkts); +    BOOST_CHECK_EQUAL( +        test_null->get_count(null_block_control::SOURCE, null_block_control::LINES), +        src_count); +    BOOST_CHECK_EQUAL( +        test_null->get_count(null_block_control::LOOP, null_block_control::LINES), +        loop_count); + +    constexpr uint32_t lpp = 3; +    constexpr uint32_t bpp = nipc * item_width / 8 * lpp; +    test_null->set_bytes_per_packet(bpp); +    copy_mem(null_block_control::REG_SRC_LINES_PER_PKT); +    copy_mem(null_block_control::REG_SRC_BYTES_PER_PKT); +    BOOST_CHECK_EQUAL(test_null->get_lines_per_packet(), lpp); +    BOOST_CHECK_EQUAL(test_null->get_bytes_per_packet(), bpp); + +    auto sca = stream_cmd_action_info::make(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    node_accessor.send_action(test_null.get(), {res_source_info::OUTPUT_EDGE, 0}, sca); +    BOOST_CHECK_EQUAL(get_mem(null_block_control::REG_CTRL_STATUS) & 0x2, 0x2); +    BOOST_REQUIRE_THROW( +        node_accessor.send_action(test_null.get(), {res_source_info::OUTPUT_EDGE, 1}, sca), +        uhd::runtime_error); +    BOOST_REQUIRE_THROW( +        node_accessor.send_action(test_null.get(), {res_source_info::INPUT_EDGE, 0}, sca), +        uhd::runtime_error); + +    stream_cmd.stream_mode = stream_cmd_t::STREAM_MODE_START_CONTINUOUS; +    test_null->issue_stream_cmd(stream_cmd); +    BOOST_CHECK_EQUAL(get_mem(null_block_control::REG_CTRL_STATUS) & 0x2, 0x2); +} +  BOOST_AUTO_TEST_CASE(test_ddc_block)  {      node_accessor_t node_accessor{}; | 
