diff options
Diffstat (limited to 'host/lib')
| -rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/replay_block_control.cpp | 528 | ||||
| -rw-r--r-- | host/lib/rfnoc/replay_block_ctrl_impl.cpp | 201 | 
3 files changed, 529 insertions, 201 deletions
| diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index 233c536bf..6c7b8c726 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -52,6 +52,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/moving_average_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/null_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/replay_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/siggen_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/split_stream_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/switchboard_block_control.cpp diff --git a/host/lib/rfnoc/replay_block_control.cpp b/host/lib/rfnoc/replay_block_control.cpp new file mode 100644 index 000000000..6067650e0 --- /dev/null +++ b/host/lib/rfnoc/replay_block_control.cpp @@ -0,0 +1,528 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/convert.hpp> +#include <uhd/exception.hpp> +#include <uhd/rfnoc/defaults.hpp> +#include <uhd/rfnoc/multichan_register_iface.hpp> +#include <uhd/rfnoc/property.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <uhd/rfnoc/replay_block_control.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/utils/math.hpp> +#include <uhdlib/utils/compat_check.hpp> +#include <uhdlib/utils/narrow.hpp> +#include <string> + +using namespace uhd::rfnoc; + +// Block compatability version +const uint16_t replay_block_control::MINOR_COMPAT = 0; +const uint16_t replay_block_control::MAJOR_COMPAT = 1; + +// NoC block address space +const uint32_t replay_block_control::REPLAY_ADDR_W = 8; +const uint32_t replay_block_control::REPLAY_BLOCK_OFFSET = +    1 << replay_block_control::REPLAY_ADDR_W; // 256 bytes + +// Register offsets +const uint32_t replay_block_control::REG_COMPAT_ADDR                = 0x00; +const uint32_t replay_block_control::REG_MEM_SIZE_ADDR              = 0x04; +const uint32_t replay_block_control::REG_REC_RESTART_ADDR           = 0x08; +const uint32_t replay_block_control::REG_REC_BASE_ADDR_LO_ADDR      = 0x10; +const uint32_t replay_block_control::REG_REC_BASE_ADDR_HI_ADDR      = 0x14; +const uint32_t replay_block_control::REG_REC_BUFFER_SIZE_LO_ADDR    = 0x18; +const uint32_t replay_block_control::REG_REC_BUFFER_SIZE_HI_ADDR    = 0x1C; +const uint32_t replay_block_control::REG_REC_FULLNESS_LO_ADDR       = 0x20; +const uint32_t replay_block_control::REG_REC_FULLNESS_HI_ADDR       = 0x24; +const uint32_t replay_block_control::REG_PLAY_BASE_ADDR_LO_ADDR     = 0x28; +const uint32_t replay_block_control::REG_PLAY_BASE_ADDR_HI_ADDR     = 0x2C; +const uint32_t replay_block_control::REG_PLAY_BUFFER_SIZE_LO_ADDR   = 0x30; +const uint32_t replay_block_control::REG_PLAY_BUFFER_SIZE_HI_ADDR   = 0x34; +const uint32_t replay_block_control::REG_PLAY_CMD_NUM_WORDS_LO_ADDR = 0x38; +const uint32_t replay_block_control::REG_PLAY_CMD_NUM_WORDS_HI_ADDR = 0x3C; +const uint32_t replay_block_control::REG_PLAY_CMD_TIME_LO_ADDR      = 0x40; +const uint32_t replay_block_control::REG_PLAY_CMD_TIME_HI_ADDR      = 0x44; +const uint32_t replay_block_control::REG_PLAY_CMD_ADDR              = 0x48; +const uint32_t replay_block_control::REG_PLAY_WORDS_PER_PKT_ADDR    = 0x4C; +const uint32_t replay_block_control::REG_PLAY_ITEM_SIZE_ADDR        = 0x50; + +// Stream commands +const uint32_t replay_block_control::PLAY_CMD_STOP       = 0; +const uint32_t replay_block_control::PLAY_CMD_FINITE     = 1; +const uint32_t replay_block_control::PLAY_CMD_CONTINUOUS = 2; + +// Mask bits +constexpr uint32_t PLAY_COMMAND_TIMED_BIT  = 31; +constexpr uint32_t PLAY_COMMAND_TIMED_MASK = uint32_t(1) << PLAY_COMMAND_TIMED_BIT; +constexpr uint32_t PLAY_COMMAND_MASK       = 3; + +// User property names +const char* const PROP_KEY_RECORD_OFFSET = "record_offset"; +const char* const PROP_KEY_RECORD_SIZE   = "record_size"; +const char* const PROP_KEY_PLAY_OFFSET   = "play_offset"; +const char* const PROP_KEY_PLAY_SIZE     = "play_size"; +const char* const PROP_KEY_PKT_SIZE      = "packet_size"; + +class replay_block_control_impl : public replay_block_control +{ +public: +    RFNOC_BLOCK_CONSTRUCTOR(replay_block_control), +        _replay_reg_iface(*this, 0, REPLAY_BLOCK_OFFSET), +        _num_input_ports(get_num_input_ports()), +        _num_output_ports(get_num_output_ports()), +        _fpga_compat(_replay_reg_iface.peek32(REG_COMPAT_ADDR)), +        _word_size( +            uint16_t((_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) >> 16) & 0xFFFF) / 8), +        _mem_size(uint64_t(1 << (_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) & 0xFFFF))) +    { +        UHD_ASSERT_THROW(get_num_input_ports() == get_num_output_ports()); +        uhd::assert_fpga_compat(MAJOR_COMPAT, +            MINOR_COMPAT, +            _fpga_compat, +            get_unique_id(), +            get_unique_id(), +            false /* Let it slide if minors mismatch */ +        ); + +        // Initialize record properties +        _record_type.reserve(_num_input_ports); +        _record_offset.reserve(_num_input_ports); +        _record_size.reserve(_num_input_ports); +        for (size_t port = 0; port < _num_input_ports; port++) { +            _register_input_props(port); +            _replay_reg_iface.poke64( +                REG_REC_BASE_ADDR_LO_ADDR, _record_offset.at(port).get(), port); +            _replay_reg_iface.poke64( +                REG_REC_BUFFER_SIZE_LO_ADDR, _record_size.at(port).get(), port); +        } + +        // Initialize playback properties +        _play_type.reserve(_num_output_ports); +        _play_offset.reserve(_num_output_ports); +        _play_size.reserve(_num_output_ports); +        _packet_size.reserve(_num_output_ports); +        for (size_t port = 0; port < _num_output_ports; port++) { +            _register_output_props(port); +            _replay_reg_iface.poke32(REG_PLAY_ITEM_SIZE_ADDR, +                uhd::convert::get_bytes_per_item(_play_type.at(port).get()), +                port); +            _replay_reg_iface.poke64( +                REG_PLAY_BASE_ADDR_LO_ADDR, _play_offset.at(port).get(), port); +            _replay_reg_iface.poke64( +                REG_PLAY_BUFFER_SIZE_LO_ADDR, _play_size.at(port).get(), port); +            _replay_reg_iface.poke32(REG_PLAY_WORDS_PER_PKT_ADDR, +                (_packet_size.at(port).get() - CHDR_MAX_LEN_HDR) / _word_size, +                port); +        } +    } + +    /************************************************************************** +     * Replay Control API +     **************************************************************************/ +    void record(const uint64_t offset, const uint64_t size, const size_t port) +    { +        set_property<uint64_t>( +            PROP_KEY_RECORD_OFFSET, offset, {res_source_info::USER, port}); +        set_property<uint64_t>(PROP_KEY_RECORD_SIZE, size, {res_source_info::USER, port}); + +        // The pointers to the new record buffer space must be set +        record_restart(port); +    } + +    void record_restart(const size_t port) +    { +        // Ensure that the buffer is properly configured before recording +        _validate_record_buffer(port); +        // Any value written to this register causes a record restart +        _replay_reg_iface.poke32(REG_REC_RESTART_ADDR, 0, port); +    } + +    void play(const uint64_t offset, +        const uint64_t size, +        const size_t port, +        const uhd::time_spec_t time_spec, +        const bool repeat) +    { +        config_play(offset, size, port); +        uhd::stream_cmd_t play_cmd = +            repeat ? uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS) +                   : uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); +        play_cmd.num_samps  = size / get_play_item_size(port); +        play_cmd.time_spec  = time_spec; +        play_cmd.stream_now = (time_spec == 0.0); +        issue_stream_cmd(play_cmd, port); +    } + +    void stop(const size_t port) +    { +        uhd::stream_cmd_t stop_cmd = +            uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +        issue_stream_cmd(stop_cmd, port); +    } + +    uint64_t get_mem_size() const +    { +        return _mem_size; +    } + +    uint64_t get_word_size() const +    { +        return _word_size; +    } + +    /************************************************************************** +     * Record Buffer State API +     **************************************************************************/ +    uint64_t get_record_offset(const size_t port) const +    { +        return _record_offset.at(port).get(); +    } + +    uint64_t get_record_size(const size_t port) const +    { +        return _record_size.at(port).get(); +    } + +    uint64_t get_record_fullness(const size_t port) +    { +        return _replay_reg_iface.peek64(REG_REC_FULLNESS_LO_ADDR, port); +    } + +    io_type_t get_record_type(const size_t port) const +    { +        return _record_type.at(port).get(); +    } + +    virtual size_t get_record_item_size(const size_t port) const +    { +        return uhd::convert::get_bytes_per_item(get_record_type(port)); +    } + +    /************************************************************************** +     * Playback State API +     **************************************************************************/ +    uint64_t get_play_offset(const size_t port) const +    { +        return _play_offset.at(port).get(); +    } + +    uint64_t get_play_size(const size_t port) const +    { +        return _play_size.at(port).get(); +    } + +    uint32_t get_max_items_per_packet(const size_t port) const +    { +        return (_packet_size.at(port).get() - CHDR_MAX_LEN_HDR) +               / get_play_item_size(port); +    } + +    uint32_t get_max_packet_size(const size_t port) const +    { +        return _packet_size.at(port).get(); +    } + +    io_type_t get_play_type(const size_t port) const +    { +        return _play_type.at(port).get(); +    } + +    size_t get_play_item_size(const size_t port) const +    { +        return uhd::convert::get_bytes_per_item(get_play_type(port)); +    } + +    /************************************************************************** +     * Advanced Record Control API calls +     *************************************************************************/ +    void set_record_type(const io_type_t type, const size_t port) +    { +        set_property<std::string>( +            PROP_KEY_TYPE, type, {res_source_info::INPUT_EDGE, port}); +    } + +    /************************************************************************** +     * Advanced Playback Control API +     **************************************************************************/ +    void config_play(const uint64_t offset, const uint64_t size, const size_t port) +    { +        set_property<uint64_t>( +            PROP_KEY_PLAY_OFFSET, offset, {res_source_info::USER, port}); +        set_property<uint64_t>(PROP_KEY_PLAY_SIZE, size, {res_source_info::USER, port}); +        _validate_play_buffer(port); +    } + +    void set_play_type(const io_type_t type, const size_t port) +    { +        set_property<std::string>( +            PROP_KEY_TYPE, type, {res_source_info::OUTPUT_EDGE, port}); +    } + +    void set_max_items_per_packet(const uint32_t ipp, const size_t port) +    { +        set_max_packet_size(CHDR_MAX_LEN_HDR + ipp * get_play_item_size(port), port); +    } + +    void set_max_packet_size(const uint32_t size, const size_t port) +    { +        set_property<uint32_t>(PROP_KEY_PKT_SIZE, size, {res_source_info::USER, port}); +    } + +    void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd, const size_t port) +    { +        // Ensure that the buffer is properly configured before issuing a stream command +        _validate_play_buffer(port); +        RFNOC_LOG_TRACE("replay_block_control_impl::issue_stream_cmd(port=" +                        << port << ", mode=" << char(stream_cmd.stream_mode) << ")"); + +        // Setup the mode to instruction flags +        const uint8_t play_cmd = [stream_cmd]() -> uint8_t { +            switch (stream_cmd.stream_mode) { +                case uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS: +                    return PLAY_CMD_STOP; // Stop playing back data +                case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE: +                case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE: +                    return PLAY_CMD_FINITE; // Play NUM_SAMPS then stop +                case uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: +                    return PLAY_CMD_CONTINUOUS; // Playback continuously over the play +                                                // buffer until stopped +                default: +                    throw uhd::value_error("Requested invalid stream command."); +            } +        }(); + +        // Calculate the number of words to transfer in NUM_SAMPS mode +        if (play_cmd == PLAY_CMD_FINITE) { +            uint64_t num_words = +                stream_cmd.num_samps * get_play_item_size(port) / get_word_size(); +            _replay_reg_iface.poke64(REG_PLAY_CMD_NUM_WORDS_LO_ADDR, num_words, port); +        } + +        // Set the time for the command +        const uint32_t timed_flag = (stream_cmd.stream_now) ? 0 : PLAY_COMMAND_TIMED_MASK; +        if (!stream_cmd.stream_now) { +            const double tick_rate = get_tick_rate(); +            UHD_LOG_DEBUG("REPLAY", +                "Using tick rate " << (tick_rate / 1e6) << " MHz to set stream command."); +            const uint64_t ticks = stream_cmd.time_spec.to_ticks(tick_rate); +            _replay_reg_iface.poke64(REG_PLAY_CMD_TIME_LO_ADDR, ticks, port); +        } + +        // Issue the stream command +        uint32_t command_word = (play_cmd & PLAY_COMMAND_MASK) | timed_flag; +        _replay_reg_iface.poke32(REG_PLAY_CMD_ADDR, command_word, port); +    } + +protected: +    // Block-specific register interface +    multichan_register_iface _replay_reg_iface; + +private: +    void _register_input_props(const size_t port) +    { +        // Get default property values +        const io_type_t default_type = IO_TYPE_SC16; +        const uint64_t record_offset = 0; +        const uint64_t record_size   = _mem_size; + +        // Initialize properties +        _record_type.emplace_back(property_t<std::string>( +            PROP_KEY_TYPE, default_type, {res_source_info::INPUT_EDGE, port})); +        _record_offset.push_back(property_t<uint64_t>( +            PROP_KEY_RECORD_OFFSET, record_offset, {res_source_info::USER, port})); +        _record_size.push_back(property_t<uint64_t>( +            PROP_KEY_RECORD_SIZE, record_size, {res_source_info::USER, port})); +        UHD_ASSERT_THROW(_record_type.size() == port + 1); +        UHD_ASSERT_THROW(_record_offset.size() == port + 1); +        UHD_ASSERT_THROW(_record_size.size() == port + 1); + +        // Register user properties +        register_property(&_record_type.at(port)); +        register_property(&_record_offset.at(port)); +        register_property(&_record_size.at(port)); + +        // Add property resolvers +        add_property_resolver({&_record_offset.at(port)}, {}, [this, port]() { +            _set_record_offset(_record_offset.at(port).get(), port); +        }); +        add_property_resolver({&_record_size.at(port)}, +            {&_record_size.at(port)}, +            [this, port]() { _set_record_size(_record_size.at(port).get(), port); }); +    } + +    void _register_output_props(const size_t port) +    { +        // Get default property values +        const io_type_t default_type = IO_TYPE_SC16; +        const uint64_t play_offset   = 0; +        const uint64_t play_size     = _mem_size; +        const uint32_t packet_size   = get_mtu({res_source_info::OUTPUT_EDGE, port}); + +        // Initialize properties +        _play_type.emplace_back(property_t<std::string>( +            PROP_KEY_TYPE, default_type, {res_source_info::OUTPUT_EDGE, port})); +        _play_offset.push_back(property_t<uint64_t>( +            PROP_KEY_PLAY_OFFSET, play_offset, {res_source_info::USER, port})); +        _play_size.push_back(property_t<uint64_t>( +            PROP_KEY_PLAY_SIZE, play_size, {res_source_info::USER, port})); +        _packet_size.push_back(property_t<uint32_t>( +            PROP_KEY_PKT_SIZE, packet_size, {res_source_info::USER, port})); +        UHD_ASSERT_THROW(_play_type.size() == port + 1); +        UHD_ASSERT_THROW(_play_offset.size() == port + 1); +        UHD_ASSERT_THROW(_play_size.size() == port + 1); +        UHD_ASSERT_THROW(_packet_size.size() == port + 1); + +        // Register user properties +        register_property(&_play_type.at(port)); +        register_property(&_play_offset.at(port)); +        register_property(&_play_size.at(port)); +        register_property(&_packet_size.at(port)); + +        // Add property resolvers +        add_property_resolver({&_play_type.at(port)}, {}, [this, port]() { +            _set_play_type(_play_type.at(port).get(), port); +        }); +        add_property_resolver({&_play_offset.at(port)}, {}, [this, port]() { +            _set_play_offset(_play_offset.at(port).get(), port); +        }); +        add_property_resolver({&_play_size.at(port)}, +            {&_play_size.at(port)}, +            [this, port]() { _set_play_size(_play_size.at(port).get(), port); }); +        add_property_resolver({&_packet_size.at(port)}, {}, [this, port]() { +            _set_packet_size(_packet_size.at(port).get(), port); +        }); +    } + +    void _set_play_type(const io_type_t type, const size_t port) +    { +        uint32_t play_item_size = uhd::convert::get_bytes_per_item(type); +        _replay_reg_iface.poke32(REG_PLAY_ITEM_SIZE_ADDR, play_item_size, port); +    } + +    void _set_record_offset(const uint64_t record_offset, const size_t port) +    { +        if ((record_offset % _word_size) != 0) { +            throw uhd::value_error("Record offset must be a multiple of word size."); +        } +        if (record_offset > _mem_size) { +            throw uhd::value_error("Record offset is out of bounds."); +        } +        _replay_reg_iface.poke64(REG_REC_BASE_ADDR_LO_ADDR, record_offset, port); +    } + +    void _set_record_size(const uint64_t record_size, const size_t port) +    { +        if ((record_size % _word_size) != 0) { +            _record_size.at(port) = record_size - (record_size % _word_size); +            throw uhd::value_error("Record buffer size must be a multiple of word size."); +        } +        _replay_reg_iface.poke64(REG_REC_BUFFER_SIZE_LO_ADDR, record_size, port); +    } + +    void _set_play_offset(const uint64_t play_offset, const size_t port) +    { +        if ((play_offset % _word_size) != 0) { +            throw uhd::value_error("Play offset must be a multiple of word size."); +        } +        if (play_offset > _mem_size) { +            throw uhd::value_error("Play offset is out of bounds."); +        } +        _replay_reg_iface.poke64(REG_PLAY_BASE_ADDR_LO_ADDR, play_offset, port); +    } + +    void _set_play_size(const uint64_t play_size, const size_t port) +    { +        if ((play_size % _word_size) != 0) { +            _play_size.at(port) = play_size - (play_size % _word_size); +            throw uhd::value_error("Play buffer size must be a multiple of word size."); +        } +        if ((play_size % get_play_item_size(port)) != 0) { +            _play_size.at(port) = play_size - (play_size % get_play_item_size(port)); +            throw uhd::value_error("Play buffer size must be a multiple of item size."); +        } +        _replay_reg_iface.poke64(REG_PLAY_BUFFER_SIZE_LO_ADDR, play_size, port); +    } + +    void _set_packet_size(const uint32_t packet_size, const size_t port) +    { +        // MTU is max payload size, header with timestamp is already accounted for +        const size_t mtu               = get_mtu({res_source_info::OUTPUT_EDGE, port}); +        const uint32_t item_size       = get_play_item_size(port); +        const uint32_t mtu_payload     = mtu - CHDR_MAX_LEN_HDR; +        const uint32_t mtu_items       = mtu_payload / item_size; +        const uint32_t ipc             = _word_size / item_size; // items per cycle +        const uint32_t max_ipp_per_mtu = mtu_items - (mtu_items % ipc); +        const uint32_t payload_size    = packet_size - CHDR_MAX_LEN_HDR; +        uint32_t ipp                   = payload_size / item_size; +        if (ipp > max_ipp_per_mtu) { +            RFNOC_LOG_WARNING("ipp value " << ipp << " exceeds MTU of " << mtu +                                           << "! Coercing to " << max_ipp_per_mtu); +            ipp = max_ipp_per_mtu; +        } +        if ((ipp % ipc) != 0) { +            ipp = ipp - (ipp % ipc); +            RFNOC_LOG_WARNING( +                "ipp must be a multiple of the block bus width! Coercing to " << ipp); +        } +        if (ipp <= 0) { +            ipp = DEFAULT_SPP; +            RFNOC_LOG_WARNING("ipp must be greater than zero! Coercing to " << ipp); +        } +        // Packet size must be a multiple of word size +        if ((packet_size % _word_size) != 0) { +            throw uhd::value_error("Packet size must be a multiple of word size."); +        } +        const uint16_t words_per_packet = +            uhd::narrow_cast<uint16_t>(ipp * item_size / _word_size); +        _replay_reg_iface.poke32( +            REG_PLAY_WORDS_PER_PKT_ADDR, uint32_t(words_per_packet), port); +    } + +    void _validate_record_buffer(const size_t port) +    { +        // The entire record buffer must be within the bounds of memory +        if ((get_record_offset(port) + get_record_size(port)) > get_mem_size()) { +            throw uhd::value_error("Record buffer goes out of bounds."); +        } +    } + +    void _validate_play_buffer(const size_t port) +    { +        // Streaming requires that the buffer size is a multiple of item size +        if ((get_play_size(port) % get_play_item_size(port)) != 0) { +            throw uhd::value_error("Play size must be must be a multiple of item size."); +        } +        // The entire play buffer must be within the bounds of memory +        if ((get_play_offset(port) + get_play_size(port)) > get_mem_size()) { +            throw uhd::value_error("Play buffer goes out of bounds."); +        } +    } + +    /************************************************************************** +     * Attributes +     *************************************************************************/ +    const size_t _num_input_ports; +    const size_t _num_output_ports; + +    // Block compat number +    const uint32_t _fpga_compat; + +    // These size params are configurable in the FPGA +    const uint16_t _word_size; +    const uint64_t _mem_size; + +    std::vector<property_t<std::string>> _record_type; +    std::vector<property_t<uint64_t>> _record_offset; +    std::vector<property_t<uint64_t>> _record_size; +    std::vector<property_t<std::string>> _play_type; +    std::vector<property_t<uint64_t>> _play_offset; +    std::vector<property_t<uint64_t>> _play_size; +    std::vector<property_t<uint32_t>> _packet_size; +}; + +UHD_RFNOC_BLOCK_REGISTER_DIRECT( +    replay_block_control, REPLAY_BLOCK, "Replay", CLOCK_KEY_GRAPH, "bus_clk") diff --git a/host/lib/rfnoc/replay_block_ctrl_impl.cpp b/host/lib/rfnoc/replay_block_ctrl_impl.cpp deleted file mode 100644 index db3721d37..000000000 --- a/host/lib/rfnoc/replay_block_ctrl_impl.cpp +++ /dev/null @@ -1,201 +0,0 @@ -// -// Copyright 2016 Ettus Research LLC -// Copyright 2018 Ettus Research, a National Instruments Company -// -// SPDX-License-Identifier: GPL-3.0-or-later -// - -#include <uhd/rfnoc/replay_block_ctrl.hpp> -#include <mutex> - -using namespace uhd; -using namespace uhd::rfnoc; - -class replay_block_ctrl_impl : public replay_block_ctrl -{ -public: -    static const uint32_t REPLAY_WORD_SIZE    = 8; // In bytes -    static const uint32_t SAMPLES_PER_WORD    = 2; -    static const uint32_t BYTES_PER_SAMPLE    = 4; -    static const uint32_t DEFAULT_BUFFER_SIZE = 32 * 1024 * 1024; -    static const uint32_t DEFAULT_WPP         = 182; -    static const uint32_t DEFAULT_SPP         = DEFAULT_WPP * SAMPLES_PER_WORD; - - -    UHD_RFNOC_BLOCK_CONSTRUCTOR(replay_block_ctrl) -    { -        _num_channels = get_input_ports().size(); -        _params.resize(_num_channels); -        for (size_t chan = 0; chan < _params.size(); chan++) { -            _params[chan].words_per_packet = DEFAULT_WPP; -            sr_write("RX_CTRL_MAXLEN", DEFAULT_WPP, chan); - -            // Configure replay channels to be adjacent DEFAULT_BUFFER_SIZE'd blocks -            _params[chan].rec_base_addr    = chan * DEFAULT_BUFFER_SIZE; -            _params[chan].play_base_addr   = chan * DEFAULT_BUFFER_SIZE; -            _params[chan].rec_buffer_size  = DEFAULT_BUFFER_SIZE; -            _params[chan].play_buffer_size = DEFAULT_BUFFER_SIZE; -            sr_write("REC_BASE_ADDR", _params[chan].rec_base_addr, chan); -            sr_write("REC_BUFFER_SIZE", _params[chan].rec_buffer_size, chan); -            sr_write("PLAY_BASE_ADDR", _params[chan].play_base_addr, chan); -            sr_write("PLAY_BUFFER_SIZE", _params[chan].play_buffer_size, chan); -        } -    } - - -    /************************************************************************** -     * API Calls -     **************************************************************************/ - -    void config_record(const uint32_t base_addr, const uint32_t size, const size_t chan) -    { -        std::lock_guard<std::mutex> lock(_mutex); -        _params[chan].rec_base_addr   = base_addr; -        _params[chan].rec_buffer_size = size; -        sr_write("REC_BASE_ADDR", base_addr, chan); -        sr_write("REC_BUFFER_SIZE", size, chan); -        sr_write("REC_RESTART", 0, chan); -    } - -    void config_play(const uint32_t base_addr, const uint32_t size, const size_t chan) -    { -        std::lock_guard<std::mutex> lock(_mutex); -        _params[chan].play_base_addr   = base_addr; -        _params[chan].play_buffer_size = size; -        sr_write("PLAY_BASE_ADDR", base_addr, chan); -        sr_write("PLAY_BUFFER_SIZE", size, chan); -    } - -    void record_restart(const size_t chan) -    { -        std::lock_guard<std::mutex> lock(_mutex); -        sr_write("REC_RESTART", 0, chan); -    } - -    uint32_t get_record_addr(const size_t chan) -    { -        return _params[chan].rec_base_addr; -    } - -    uint32_t get_record_size(const size_t chan) -    { -        return _params[chan].rec_buffer_size; -    } - -    uint32_t get_record_fullness(const size_t chan) -    { -        return user_reg_read32("REC_FULLNESS", chan); -    } - -    uint32_t get_play_addr(const size_t chan) -    { -        return _params[chan].play_base_addr; -    } - -    uint32_t get_play_size(const size_t chan) -    { -        return _params[chan].play_buffer_size; -    } - -    void set_words_per_packet(const uint32_t num_words, const size_t chan) -    { -        std::lock_guard<std::mutex> lock(_mutex); -        _params[chan].words_per_packet = num_words; -        sr_write("RX_CTRL_MAXLEN", num_words, chan); -    } - -    uint32_t get_words_per_packet(const size_t chan) -    { -        return _params[chan].words_per_packet; -    } - -    void play_halt(const size_t chan) -    { -        sr_write("RX_CTRL_HALT", 1, chan); -    } - - -    /*************************************************************************** -     * Radio-like Streamer -     **************************************************************************/ - -    void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd, const size_t chan) -    { -        std::lock_guard<std::mutex> lock(_mutex); -        UHD_RFNOC_BLOCK_TRACE() << "replay_block_ctrl_impl::issue_stream_cmd() " << chan -                                << " " << char(stream_cmd.stream_mode); - -        if (not(_rx_streamer_active.count(chan) and _rx_streamer_active.at(chan))) { -            UHD_RFNOC_BLOCK_TRACE() -                << "replay_block_ctrl_impl::issue_stream_cmd() called on inactive " -                   "channel. Skipping."; -            return; -        } - -        constexpr size_t max_num_samps = 0x0fffffff; -        if (stream_cmd.num_samps > max_num_samps) { -            UHD_LOG_ERROR("REPLAY", -                "Requesting too many samples in a single burst! " -                "Requested " -                    + std::to_string(stream_cmd.num_samps) -                    + ", maximum " -                      "is " -                    + std::to_string(max_num_samps) + "."); -            throw uhd::value_error("Requested too many samples in a single burst."); -        } - -        // Setup the mode to instruction flags -        typedef std::tuple<bool, bool, bool, bool> inst_t; -        static const std::map<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst{ -            // reload, chain, samps, stop -            {stream_cmd_t::STREAM_MODE_START_CONTINUOUS, -                inst_t(true, true, false, false)}, -            {stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, -                inst_t(false, false, false, true)}, -            {stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, -                inst_t(false, false, true, false)}, -            {stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, -                inst_t(false, true, true, false)}}; - -        // Setup the instruction flag values -        bool inst_reload, inst_chain, inst_samps, inst_stop; -        std::tie(inst_reload, inst_chain, inst_samps, inst_stop) = -            mode_to_inst.at(stream_cmd.stream_mode); - -        // Calculate how many words to transfer at a time in CONTINUOUS mode -        uint32_t cont_burst_size = -            (_params[chan].play_buffer_size > _params[chan].words_per_packet) -                ? _params[chan].words_per_packet -                : _params[chan].play_buffer_size; - -        // Calculate the number of words to transfer in NUM_SAMPS mode -        uint32_t num_words = stream_cmd.num_samps / SAMPLES_PER_WORD; - -        // Calculate the word from flags and length -        const uint32_t cmd_word = -            0 | (uint32_t(stream_cmd.stream_now ? 1 : 0) << 31) -            | (uint32_t(inst_chain ? 1 : 0) << 30) | (uint32_t(inst_reload ? 1 : 0) << 29) -            | (uint32_t(inst_stop ? 1 : 0) << 28) -            | (inst_samps ? num_words : (inst_stop ? 0 : cont_burst_size)); - -        // Issue the stream command -        sr_write("RX_CTRL_COMMAND", cmd_word, chan); -    } - -private: -    struct replay_params_t -    { -        size_t words_per_packet; -        uint32_t rec_base_addr; -        uint32_t rec_buffer_size; -        uint32_t play_base_addr; -        uint32_t play_buffer_size; -    }; - -    size_t _num_channels; -    std::vector<replay_params_t> _params; - -    std::mutex _mutex; -}; - -UHD_RFNOC_BLOCK_REGISTER(replay_block_ctrl, "Replay"); | 
