aboutsummaryrefslogtreecommitdiffstats
path: root/host/include
diff options
context:
space:
mode:
authorAshish Chaudhari <ashish@ettus.com>2019-05-20 17:14:20 -0700
committerMartin Braun <martin.braun@ettus.com>2019-11-26 11:49:15 -0800
commit0b698810a163e3986939341fee5014fc6ad7e7f9 (patch)
tree22e2c122542928e29a2a0a8f6d383ad12e65b6a8 /host/include
parentad4004f1f78d5b64dae50b6e456d0206e824978f (diff)
downloaduhd-0b698810a163e3986939341fee5014fc6ad7e7f9.tar.gz
uhd-0b698810a163e3986939341fee5014fc6ad7e7f9.tar.bz2
uhd-0b698810a163e3986939341fee5014fc6ad7e7f9.zip
rfnoc: Added impl for reg_iface and ctrl_endpoint
- Added new register_iface class that translates high-level peek/poke calls into CHDR control payloads - Added new chdr_ctrl_endpoint class that emulates a control stream endpoint in SW. It can create and handle multiple register interfaces
Diffstat (limited to 'host/include')
-rw-r--r--host/include/uhd/rfnoc/CMakeLists.txt1
-rw-r--r--host/include/uhd/rfnoc/register_iface.hpp222
2 files changed, 223 insertions, 0 deletions
diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt
index 052d44090..319240578 100644
--- a/host/include/uhd/rfnoc/CMakeLists.txt
+++ b/host/include/uhd/rfnoc/CMakeLists.txt
@@ -39,6 +39,7 @@ if(ENABLE_RFNOC)
replay_block_ctrl.hpp
siggen_block_ctrl.hpp
window_block_ctrl.hpp
+ register_iface.hpp
DESTINATION ${INCLUDE_DIR}/uhd/rfnoc
COMPONENT headers
)
diff --git a/host/include/uhd/rfnoc/register_iface.hpp b/host/include/uhd/rfnoc/register_iface.hpp
new file mode 100644
index 000000000..7be21affd
--- /dev/null
+++ b/host/include/uhd/rfnoc/register_iface.hpp
@@ -0,0 +1,222 @@
+//
+// Copyright 2019 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_REGISTER_IFACE_HPP
+#define INCLUDED_LIBUHD_RFNOC_REGISTER_IFACE_HPP
+
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace uhd { namespace rfnoc {
+
+/*! A software interface to access low-level registers in a NoC block.
+ *
+ * This interface supports the following:
+ * - Writing and reading registers
+ * - Hardware timed delays (for time sequencing operations)
+ * - Asynchronous messages (where a block requests a "register write" in software)
+ *
+ * This class has no public factory function or constructor.
+ */
+class register_iface
+{
+public:
+ using sptr = std::shared_ptr<register_iface>;
+
+ virtual ~register_iface() = default;
+
+ /*! Callback function for an asynchronous message.
+ * When a block in the FPGA sends an asynchronous message to the software,
+ * the async message callback function is called. An async message can be
+ * modelled as a simple register write (key-value pair with addr/data) that
+ * is initiated by the FPGA.
+ */
+ using async_msg_callback_t = std::function<void(uint32_t addr, uint32_t data)>;
+
+ /*! Write a 32-bit register implemented in the NoC block.
+ *
+ * \param addr The byte address of the register to write to (truncated to 20 bits).
+ * \param data New value of this register.
+ * \param time The time at which the transaction should be executed.
+ * \param ack Should transaction completion be acknowledged?
+ *
+ * \throws op_failed if an ACK is requested and the transaction fails
+ * \throws op_timeout if an ACK is requested and no response is received
+ * \throws op_seqerr if an ACK is requested and a sequence error occurs
+ * \throws op_timeerr if an ACK is requested and a time error occurs (late command)
+ */
+ virtual void poke32(uint32_t addr,
+ uint32_t data,
+ uhd::time_spec_t time = uhd::time_spec_t::ASAP,
+ bool ack = false) = 0;
+
+ /*! Write multiple 32-bit registers implemented in the NoC block.
+ *
+ * This method should be called when multiple writes need to happen that are
+ * at non-consecutive addresses. For consecutive writes, cf. block_poke32().
+ *
+ * \param addrs The byte addresses of the registers to write to
+ * (each truncated to 20 bits).
+ * \param data New values of these registers. The lengths of data and addr
+ * must match.
+ * \param time The time at which the first transaction should be executed.
+ * \param ack Should transaction completion be acknowledged?
+ *
+ * \throws uhd::value_error if lengths of data and addr don't match
+ * \throws op_failed if an ACK is requested and the transaction fails
+ * \throws op_timeout if an ACK is requested and no response is received
+ * \throws op_seqerr if an ACK is requested and a sequence error occurs
+ * \throws op_timeerr if an ACK is requested and a time error occurs (late command)
+ */
+ virtual void multi_poke32(const std::vector<uint32_t> addrs,
+ const std::vector<uint32_t> data,
+ uhd::time_spec_t time = uhd::time_spec_t::ASAP,
+ bool ack = false) = 0;
+
+ /*! Write multiple consecutive 32-bit registers implemented in the NoC block.
+ *
+ * This function will only allow writes to adjacent registers, in increasing
+ * order. If addr is set to 0, and the length of data is 4, then this method
+ * will trigger four writes, in order, to addresses 0, 4, 8, 12.
+ * For arbitrary addresses, cf. multi_poke32().
+ *
+ * Note: There is no guarantee that under the hood, the implementation won't
+ * separate the writes.
+ *
+ * \param first_addr The byte addresses of the first register to write
+ * \param data New values of these registers
+ * \param time The time at which the first transaction should be executed.
+ * \param ack Should transaction completion be acknowledged?
+ *
+ * \throws op_failed if an ACK is requested and the transaction fails
+ * \throws op_timeout if an ACK is requested and no response is received
+ * \throws op_seqerr if an ACK is requested and a sequence error occurs
+ * \throws op_timeerr if an ACK is requested and a time error occurs (late command)
+ */
+ virtual void block_poke32(uint32_t first_addr,
+ const std::vector<uint32_t> data,
+ uhd::time_spec_t time = uhd::time_spec_t::ASAP,
+ bool ack = false) = 0;
+
+ /*! Read a 32-bit register implemented in the NoC block.
+ *
+ * \param addr The byte address of the register to read from (truncated to 20 bits).
+ * \param time The time at which the transaction should be executed.
+ * \return data New value of this register.
+ *
+ * \throws op_failed if the transaction fails
+ * \throws op_timeout if no response is received
+ * \throws op_seqerr if a sequence error occurs
+ */
+ virtual uint32_t peek32(uint32_t addr, time_spec_t time = uhd::time_spec_t::ASAP) = 0;
+
+ /*! Read multiple 32-bit consecutive registers implemented in the NoC block.
+ *
+ * \param first_addr The byte address of the first register to read from
+ * (truncated to 20 bits).
+ * \param length The number of 32-bit values to read
+ * \param time The time at which the transaction should be executed.
+ * \return data New value of this register.
+ *
+ * Example: If \p first_addr is set to 0, and length is 4, then this
+ * function will return a vector of length 4, with the content of registers
+ * at addresses 0, 4, 8, and 12, respectively.
+ *
+ * Note: There is no guarantee that under the hood, the implementation won't
+ * separate the reads.
+ *
+ * \throws op_failed if the transaction fails
+ * \throws op_timeout if no response is received
+ * \throws op_seqerr if a sequence error occurs
+ */
+ virtual std::vector<uint32_t> block_peek32(uint32_t first_addr,
+ size_t length,
+ time_spec_t time = uhd::time_spec_t::ASAP) = 0;
+
+ /*! Poll a 32-bit register until its value for all bits in mask match data&mask
+ *
+ * This will insert a command into the command queue to wait until a
+ * register is of a certain value. This can be used, e.g., to poll for a
+ * lock pin before executing the next command. It is related to sleep(),
+ * except it has a condition to wait on, rather than an unconditional stall
+ * duration. The timeout is hardware-timed.
+ * If the register does not attain the requested value within the requested
+ * duration, ${something bad happens}.
+ *
+ * Example: Assume readback register 16 is a status register, and bit 0
+ * indicates a lock is in place (i.e., we want it to be 1) and bit 1 is an
+ * error flag (i.e., we want it to be 0). The previous command can modify
+ * the state of the block, so we give it 1ms to settle. In that case, the
+ * call would be thus:
+ *
+ * ~~~{.cpp}
+ * // iface is a register_iface::sptr:
+ * iface->poll32(16, 0x1, 0x3, 1e-3);
+ * ~~~
+ *
+ * \param addr The byte address of the register to read from (truncated to 20 bits).
+ * \param data The values that the register must have
+ * \param mask The bitmask that is applied before checking the readback
+ * value
+ * \param timeout The max duration that the register is allowed to take
+ * before reaching its new state.
+ * \param time When the poll should be executed
+ * \param ack Should transaction completion be acknowledged? This is
+ * typically only necessary if the software needs a condition to
+ * be fulfilled before continueing, or during debugging.
+ *
+ * \throws op_failed if an ACK is requested and the transaction fails
+ * \throws op_timeout if an ACK is requested and no response is received
+ * \throws op_seqerr if an ACK is requested and a sequence error occurs
+ * \throws op_timeerr if an ACK is requested and a time error occurs (late command)
+ */
+ virtual void poll32(uint32_t addr,
+ uint32_t data,
+ uint32_t mask,
+ time_spec_t timeout,
+ time_spec_t time = uhd::time_spec_t::ASAP,
+ bool ack = false) = 0;
+
+
+ /*! Send a command to halt (block) the control bus for a specified time.
+ * This is a hardware-timed sleep.
+ *
+ * \param duration The amount of time to sleep.
+ * \param ack Should transaction completion be acknowledged?
+ *
+ * \throws op_failed if an ACK is requested and the transaction fails
+ * \throws op_timeout if an ACK is requested and no response is received
+ * \throws op_seqerr if an ACK is requested and a sequence error occurs
+ */
+ virtual void sleep(time_spec_t duration, bool ack = false) = 0;
+
+ /*! Register a callback function for when an async message is received
+ *
+ * Only one callback function can be registered. When calling this multiple
+ * times, only the last callback will be accepted.
+ *
+ * \param callback_f The function to call when an asynchronous message is received.
+ */
+ virtual void register_async_msg_handler(async_msg_callback_t callback_f) = 0;
+
+ /*! Set a policy that governs the operational parameters of this register bus.
+ * Policies can be used to make tradeoffs between performance, resilience, latency,
+ * etc.
+ *
+ * \param name The name of the policy to apply
+ * \param args Additional arguments to pass to the policy governor
+ */
+ virtual void set_policy(const std::string& name, const uhd::device_addr_t& args) = 0;
+
+}; // class register_iface
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_REGISTER_IFACE_HPP */