diff options
author | michael-west <michael.west@ettus.com> | 2022-02-13 18:17:08 -0800 |
---|---|---|
committer | Aaron Rossetto <aaron.rossetto@ni.com> | 2022-04-01 13:33:08 -0700 |
commit | ac5ddf9d0e2d110a9ead6eeadb38255b489af787 (patch) | |
tree | 325646705b734fc0af35ef77c10462d279edb928 | |
parent | 1545d3ff05f14b8dd175736b326fe6cae7dc830d (diff) | |
download | uhd-ac5ddf9d0e2d110a9ead6eeadb38255b489af787.tar.gz uhd-ac5ddf9d0e2d110a9ead6eeadb38255b489af787.tar.bz2 uhd-ac5ddf9d0e2d110a9ead6eeadb38255b489af787.zip |
uhd: Replay block version 1.1
- Add ability to get current record position.
- Add ability to get current play position.
- Track space in play command FIFO and throw uhd::op_failed error when
command requested would overflow the command FIFO.
Signed-off-by: michael-west <michael.west@ettus.com>
-rw-r--r-- | host/include/uhd/rfnoc/replay_block_control.hpp | 23 | ||||
-rw-r--r-- | host/lib/rfnoc/replay_block_control.cpp | 61 | ||||
-rw-r--r-- | host/tests/rfnoc_block_tests/replay_block_test.cpp | 100 |
3 files changed, 166 insertions, 18 deletions
diff --git a/host/include/uhd/rfnoc/replay_block_control.hpp b/host/include/uhd/rfnoc/replay_block_control.hpp index 615f6e294..ba8658a5c 100644 --- a/host/include/uhd/rfnoc/replay_block_control.hpp +++ b/host/include/uhd/rfnoc/replay_block_control.hpp @@ -139,6 +139,11 @@ public: static const uint32_t REG_PLAY_CMD_ADDR; static const uint32_t REG_PLAY_WORDS_PER_PKT_ADDR; static const uint32_t REG_PLAY_ITEM_SIZE_ADDR; + static const uint32_t REG_REC_POS_LO_ADDR; + static const uint32_t REG_REC_POS_HI_ADDR; + static const uint32_t REG_PLAY_POS_LO_ADDR; + static const uint32_t REG_PLAY_POS_HI_ADDR; + static const uint32_t REG_PLAY_CMD_FIFO_SPACE_ADDR; static const uint32_t PLAY_CMD_STOP; static const uint32_t PLAY_CMD_FINITE; @@ -212,6 +217,7 @@ public: * just once. If set to true, stop() must be called to stop * the play back. * \throws uhd::value_error if offset+size exceeds the available memory. + * \throws uhd::op_failed Too many play commands are queued. */ virtual void play(const uint64_t offset, const uint64_t size, @@ -270,6 +276,13 @@ public: */ virtual uint64_t get_record_fullness(const size_t port = 0) = 0; + /*! Get the current record position + * + * \param port Which output port of the replay block to use + * \returns the byte address of the current record position + */ + virtual uint64_t get_record_position(const size_t port = 0) = 0; + /*! Get the current record data type * * \param port Which input port of the replay block to use @@ -317,6 +330,13 @@ public: */ virtual uint64_t get_play_size(const size_t port = 0) const = 0; + /*! Get the current playback position + * + * \param port Which output port of the replay block to use + * \returns the byte address of the current playback position + */ + virtual uint64_t get_play_position(const size_t port = 0) = 0; + /*! Get the maximum number of items in a packet * * \param port Which output port of the replay block to use @@ -430,7 +450,7 @@ public: * Issue stream commands to start or stop playback from the configured playback * buffer. Supports * STREAM_MODE_START_CONTINUOUS to start continuous repeating playback, - * STREAM_MODE_START_NUM_SAMPS_AND_DONE to play the given number of samples once, and + * STREAM_MODE_NUM_SAMPS_AND_DONE to play the given number of samples once, and * STREAM_MODE_STOP_CONTINUOUS to stop all playback immediately. * If a time_spec is supplied, it is placed in the header of the first packet produced * for that command. Commands are queued and executed in order. A @@ -439,6 +459,7 @@ public: * * \param stream_cmd The command to execute * \param port Which output port of the replay block to use + * \throws uhd::op_failed Too many commands are queued. */ virtual void issue_stream_cmd( const uhd::stream_cmd_t& stream_cmd, const size_t port = 0) = 0; diff --git a/host/lib/rfnoc/replay_block_control.cpp b/host/lib/rfnoc/replay_block_control.cpp index 2ee31de93..f7497c657 100644 --- a/host/lib/rfnoc/replay_block_control.cpp +++ b/host/lib/rfnoc/replay_block_control.cpp @@ -21,7 +21,7 @@ using namespace uhd::rfnoc; // Block compatability version -const uint16_t replay_block_control::MINOR_COMPAT = 0; +const uint16_t replay_block_control::MINOR_COMPAT = 1; const uint16_t replay_block_control::MAJOR_COMPAT = 1; // NoC block address space @@ -50,6 +50,11 @@ 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; +const uint32_t replay_block_control::REG_REC_POS_LO_ADDR = 0x54; +const uint32_t replay_block_control::REG_REC_POS_HI_ADDR = 0x58; +const uint32_t replay_block_control::REG_PLAY_POS_LO_ADDR = 0x5C; +const uint32_t replay_block_control::REG_PLAY_POS_HI_ADDR = 0x60; +const uint32_t replay_block_control::REG_PLAY_CMD_FIFO_SPACE_ADDR = 0x64; // Stream commands const uint32_t replay_block_control::PLAY_CMD_STOP = 0; @@ -81,7 +86,8 @@ public: _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(1ULL << (_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) & 0xFFFF))) + _mem_size( + uint64_t(1ULL << (_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) & 0xFFFF))) { if (get_num_input_ports() != get_num_output_ports()) { throw uhd::assertion_error( @@ -169,6 +175,7 @@ public: _play_size.reserve(_num_output_ports); _packet_size.reserve(_num_output_ports); _atomic_item_size_out.reserve(_num_output_ports); + _cmd_fifo_spaces.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, @@ -181,6 +188,11 @@ public: _replay_reg_iface.poke32(REG_PLAY_WORDS_PER_PKT_ADDR, (_packet_size.at(port).get() - get_chdr_hdr_len()) / _word_size, port); + // The register to get the command FIFO space was added in v1.1 + if (_fpga_compat >= 0x00010001) { + _cmd_fifo_spaces[port] = _replay_reg_iface.peek32( + REG_PLAY_CMD_FIFO_SPACE_ADDR, port); + } } } @@ -256,6 +268,16 @@ public: return _replay_reg_iface.peek64(REG_REC_FULLNESS_LO_ADDR, port); } + uint64_t get_record_position(const size_t port) override + { + if (_fpga_compat < 0x00010001) { + throw uhd::not_implemented_error("Replay block version 1.1 or " + "greater required to get record position. " + "Update the FPGA image to get this feature."); + } + return _replay_reg_iface.peek64(REG_REC_POS_LO_ADDR, port); + } + io_type_t get_record_type(const size_t port) const override { return _record_type.at(port).get(); @@ -285,6 +307,16 @@ public: return _play_size.at(port).get(); } + uint64_t get_play_position(const size_t port) override + { + if (_fpga_compat < 0x00010001) { + throw uhd::not_implemented_error( + "Replay block version 1.1 or greater required to get play position. " + "Update the FPGA image to get this feature."); + } + return _replay_reg_iface.peek64(REG_PLAY_POS_LO_ADDR, port); + } + uint32_t get_max_items_per_packet(const size_t port) const override { return (_packet_size.at(port).get() - get_chdr_hdr_len()) @@ -371,6 +403,18 @@ public: } }(); + // The register to get the command FIFO space was added in v1.1 + if (_fpga_compat >= 0x00010001) { + // Make sure the command queue has space. Allow stop commands to pass. + if (not play_cmd == PLAY_CMD_STOP and _cmd_fifo_spaces[port] == 0) { + _cmd_fifo_spaces[port] = _replay_reg_iface.peek32( + REG_PLAY_CMD_FIFO_SPACE_ADDR, port); + if (_cmd_fifo_spaces[port] == 0) { + throw uhd::op_failed("[Replay] Play command queue is full"); + } + } + } + // Calculate the number of words to transfer in NUM_SAMPS mode if (play_cmd == PLAY_CMD_FINITE) { uint64_t num_words = @@ -391,6 +435,17 @@ public: // 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); + + // The register to get the command FIFO space was added in v1.1 + if (_fpga_compat >= 0x00010001) { + if (play_cmd == PLAY_CMD_STOP) { + // The stop command will clear the FIFO, so reset the space value + _cmd_fifo_spaces[port] = _replay_reg_iface.peek32( + REG_PLAY_CMD_FIFO_SPACE_ADDR, port); + } else { + _cmd_fifo_spaces[port]--; + } + } } protected: @@ -672,6 +727,8 @@ private: std::vector<property_t<size_t>> _atomic_item_size_in; std::vector<property_t<size_t>> _atomic_item_size_out; + std::vector<size_t> _cmd_fifo_spaces; + // Message queues for async data uhd::transport::bounded_buffer<uhd::async_metadata_t> _playback_msg_queue{ ASYNC_MSG_QUEUE_SIZE}; diff --git a/host/tests/rfnoc_block_tests/replay_block_test.cpp b/host/tests/rfnoc_block_tests/replay_block_test.cpp index 94f1ce882..7a4f99e43 100644 --- a/host/tests/rfnoc_block_tests/replay_block_test.cpp +++ b/host/tests/rfnoc_block_tests/replay_block_test.cpp @@ -22,6 +22,8 @@ using namespace uhd::rfnoc; // Redeclare this here, since it's only defined outside of UHD_API noc_block_base::make_args_t::~make_args_t() = default; +static const size_t CMD_Q_MAX = 32; + /* * This class extends mock_reg_iface_t by adding a constructor that initializes some of * the read memory to contain the memory size for the replay block. This is important, @@ -37,24 +39,29 @@ public: replay_mock_reg_iface_t(size_t mem_addr_size, size_t word_size, size_t num_channels) { for (size_t chan = 0; chan < num_channels; chan++) { - const uint32_t reg_compat = - replay_block_control::REG_COMPAT_ADDR - + chan * replay_block_control::REPLAY_BLOCK_OFFSET; + const uint32_t base = chan * replay_block_control::REPLAY_BLOCK_OFFSET; + const uint32_t reg_compat = base + + replay_block_control::REG_COMPAT_ADDR; + const uint32_t reg_mem_size = base + + replay_block_control::REG_MEM_SIZE_ADDR; + const uint32_t reg_rec_fullness = base + + replay_block_control::REG_REC_FULLNESS_LO_ADDR; + const uint32_t reg_rec_position = base + + replay_block_control::REG_REC_POS_LO_ADDR; + const uint32_t reg_play_position = base + + replay_block_control::REG_PLAY_POS_LO_ADDR; + const uint32_t reg_play_fifo_space = base + + replay_block_control::REG_PLAY_CMD_FIFO_SPACE_ADDR; read_memory[reg_compat] = (replay_block_control::MINOR_COMPAT | (replay_block_control::MAJOR_COMPAT << 16)); - } - for (size_t chan = 0; chan < num_channels; chan++) { - const uint32_t reg_mem_size = - replay_block_control::REG_MEM_SIZE_ADDR - + chan * replay_block_control::REPLAY_BLOCK_OFFSET; read_memory[reg_mem_size] = (mem_addr_size | (word_size << 16)); - } - for (size_t chan = 0; chan < num_channels; chan++) { - const uint32_t reg_rec_fullness = - replay_block_control::REG_REC_FULLNESS_LO_ADDR - + chan * replay_block_control::REPLAY_BLOCK_OFFSET; - read_memory[reg_rec_fullness] = 0x0010; - read_memory[reg_rec_fullness + 4] = 0x0000; + read_memory[reg_rec_fullness] = 0x0010; + read_memory[reg_rec_fullness + 4] = 0x0000; + read_memory[reg_rec_position] = 0xBEEF; + read_memory[reg_rec_position + 4] = 0xDEAD; + read_memory[reg_play_position] = 0xCAFE; + read_memory[reg_play_position + 4] = 0xFEED; + read_memory[reg_play_fifo_space] = CMD_Q_MAX; } } }; @@ -282,6 +289,20 @@ BOOST_FIXTURE_TEST_CASE(replay_test_record, replay_block_fixture) } } +/* + * This test case checks the record position. + */ +BOOST_FIXTURE_TEST_CASE(replay_test_record_position, replay_block_fixture) +{ + for (size_t port = 0; port < num_input_ports; port++) { + const uint32_t reg_rec_position = + get_addr(replay_block_control::REG_REC_POS_LO_ADDR, port); + uint64_t rec_pos = reg_iface->read_memory[reg_rec_position] | + (uint64_t(reg_iface->read_memory[reg_rec_position + 4]) << 32); + BOOST_CHECK_EQUAL(test_replay->get_record_position(port), rec_pos); + } +} + /************************************************************************** * Playback tests *************************************************************************/ @@ -729,6 +750,55 @@ BOOST_FIXTURE_TEST_CASE(replay_test_play, replay_block_fixture) } /* + * This test case checks the play position. + */ +BOOST_FIXTURE_TEST_CASE(replay_test_play_position, replay_block_fixture) +{ + for (size_t port = 0; port < num_input_ports; port++) { + const uint32_t reg_play_position = + get_addr(replay_block_control::REG_PLAY_POS_LO_ADDR, port); + uint64_t play_pos = reg_iface->read_memory[reg_play_position] | + (uint64_t(reg_iface->read_memory[reg_play_position + 4]) << 32); + BOOST_CHECK_EQUAL(test_replay->get_play_position(port), play_pos); + } +} + +/* + * This test case checks to make sure play commands throw an error if the + * command queue is full. + */ +BOOST_FIXTURE_TEST_CASE(replay_test_play_cmd_limit, replay_block_fixture) +{ + for (size_t port = 0; port < num_input_ports; port++) { + const uint32_t reg_play_cmd_fifo_space = + get_addr(replay_block_control::REG_PLAY_CMD_FIFO_SPACE_ADDR, port); + + // Issue stop to clear command queue + test_replay->stop(port); + + // Fill the command queue + for (size_t i = 0; i < CMD_Q_MAX; i++) { + test_replay->play(0, 0, port); + } + reg_iface->read_memory[reg_play_cmd_fifo_space] = 0; + + // Make sure the next command throws + BOOST_CHECK_THROW(test_replay->play(0, 0, port), uhd::op_failed); + + reg_iface->read_memory[reg_play_cmd_fifo_space] = CMD_Q_MAX; + + // Issue stop to clear the queue and reset + test_replay->stop(port); + + // Run once more to confirm no error is thrown + test_replay->play(0, 0, port); + + // Issue stop to clear the queue and reset + test_replay->stop(port); + } +} + +/* * This test case ensures that the Replay Block can be added to an RFNoC graph. */ BOOST_FIXTURE_TEST_CASE(replay_test_graph, replay_block_fixture) |