aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormichael-west <michael.west@ettus.com>2022-02-13 18:17:08 -0800
committerAaron Rossetto <aaron.rossetto@ni.com>2022-04-01 13:33:08 -0700
commitac5ddf9d0e2d110a9ead6eeadb38255b489af787 (patch)
tree325646705b734fc0af35ef77c10462d279edb928
parent1545d3ff05f14b8dd175736b326fe6cae7dc830d (diff)
downloaduhd-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.hpp23
-rw-r--r--host/lib/rfnoc/replay_block_control.cpp61
-rw-r--r--host/tests/rfnoc_block_tests/replay_block_test.cpp100
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)