diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2019-05-20 17:14:20 -0700 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2019-11-26 11:49:15 -0800 |
commit | 0b698810a163e3986939341fee5014fc6ad7e7f9 (patch) | |
tree | 22e2c122542928e29a2a0a8f6d383ad12e65b6a8 /host/include | |
parent | ad4004f1f78d5b64dae50b6e456d0206e824978f (diff) | |
download | uhd-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.txt | 1 | ||||
-rw-r--r-- | host/include/uhd/rfnoc/register_iface.hpp | 222 |
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 */ |