diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/include/uhd/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/axi_ram_fifo_2x64.yml | 82 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/axi_ram_fifo_4x64.yml | 114 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/core/io_signatures.yml | 288 | ||||
| -rw-r--r-- | host/include/uhd/rfnoc/dmafifo_block_control.hpp | 25 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp | 73 | ||||
| -rw-r--r-- | host/lib/rfnoc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/rfnoc/dmafifo_block_control.cpp | 71 | ||||
| -rw-r--r-- | host/lib/rfnoc/node.cpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/cores/dma_fifo_core_3000.cpp | 604 | 
10 files changed, 860 insertions, 401 deletions
| diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt index 20c8cc38a..9abacbeaa 100644 --- a/host/include/uhd/rfnoc/CMakeLists.txt +++ b/host/include/uhd/rfnoc/CMakeLists.txt @@ -33,6 +33,7 @@ UHD_INSTALL(FILES      block_control.hpp      ddc_block_control.hpp      duc_block_control.hpp +    dmafifo_block_control.hpp      null_block_control.hpp      radio_control.hpp diff --git a/host/include/uhd/rfnoc/blocks/axi_ram_fifo_2x64.yml b/host/include/uhd/rfnoc/blocks/axi_ram_fifo_2x64.yml new file mode 100644 index 000000000..ddb7eed28 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/axi_ram_fifo_2x64.yml @@ -0,0 +1,82 @@ +schema: rfnoc_modtool_args +module_name: axi_ram_fifo +version: 1.0 +rfnoc_version: 1.0 +chdr_width: 64 +noc_id: 0xF1F00000 + +clocks: +  - name: rfnoc_chdr +    freq: "[]" +  - name: rfnoc_ctrl +    freq: "[]" + +control: +  sw_iface: nocscript +  fpga_iface: ctrlport +  interface_direction: master_slave +  fifo_depth: 32 +  clk_domain: rfnoc_ctrl +  ctrlport: +    byte_mode: True +    timed: False +    has_status: False + +# The parameters section lists parameters that get added to the generated +# Verilog for the module instantiation. Any parameter listed here may be set to +# different value in the image builder YAML file. +parameters: +  NUM_PORTS: 2 +  MEM_DATA_W: 64 +  MEM_ADDR_W: 30 +  FIFO_ADDR_BASE: "{30'h02000000, 30'h00000000}" +  FIFO_ADDR_MASK: "{30'h01FFFFFF, 30'h01FFFFFF}" +  MEM_CLK_RATE: "300e6" + +data: +  fpga_iface: axis_chdr +  clk_domain: rfnoc_chdr +  mtu: 1024 +  inputs: +    port0: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port1: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +  outputs: +    port0: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port1: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ + +io_ports: +  axi_ram: +    type: axi4_mm_2x64_4g +    drive: slave + +registers: + +properties: diff --git a/host/include/uhd/rfnoc/blocks/axi_ram_fifo_4x64.yml b/host/include/uhd/rfnoc/blocks/axi_ram_fifo_4x64.yml new file mode 100644 index 000000000..8fed9d6a2 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/axi_ram_fifo_4x64.yml @@ -0,0 +1,114 @@ +schema: rfnoc_modtool_args +module_name: axi_ram_fifo +version: 1.0 +rfnoc_version: 1.0 +chdr_width: 64 +noc_id: 0xF1F00000 + +clocks: +  - name: rfnoc_chdr +    freq: "[]" +  - name: rfnoc_ctrl +    freq: "[]" + +control: +  sw_iface: nocscript +  fpga_iface: ctrlport +  interface_direction: master_slave +  fifo_depth: 32 +  clk_domain: rfnoc_ctrl +  ctrlport: +    byte_mode: True +    timed: False +    has_status: False + +# The parameters section lists parameters that get added to the generated +# Verilog for the module instantiation. Any parameter listed here may be set to +# different value in the image builder YAML file. +parameters: +  NUM_PORTS: 4 +  MEM_DATA_W: 64 +  MEM_ADDR_W: 32 +  FIFO_ADDR_BASE: "{32'hC0000000, 32'h80000000, 32'h40000000, 32'h00000000}" +  FIFO_ADDR_MASK: "{32'h3FFFFFFF, 32'h3FFFFFFF, 32'h3FFFFFFF, 32'h3FFFFFFF}" +  MEM_CLK_RATE: "300e6" + +data: +  fpga_iface: axis_chdr +  clk_domain: rfnoc_chdr +  mtu: 1024 +  inputs: +    port0: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port1: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port2: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port3: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +  outputs: +    port0: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port1: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port2: +      index: 0 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ +    port3: +      index: 1 +      item_width: 32 +      nipc: 2 +      context_fifo_depth: 1 +      payload_fifo_depth: 1 +      format: int32 +      mdata_sig: ~ + +io_ports: +  axi_ram: +    type: axi4_mm_4x64_4g +    drive: slave + +registers: + +properties: diff --git a/host/include/uhd/rfnoc/core/io_signatures.yml b/host/include/uhd/rfnoc/core/io_signatures.yml index ba7721e3e..57e1b11c9 100644 --- a/host/include/uhd/rfnoc/core/io_signatures.yml +++ b/host/include/uhd/rfnoc/core/io_signatures.yml @@ -77,3 +77,291 @@ x300_radio:    - name: radio_tx_running      type: to-master      width: 2 + +# A 2-port AXI4 memory-mapped interface with 64-bit data, and 2 GiB address +# space (32-bit). +axi4_mm_2x64_4g: +  type: master-slave +  ports: +  - name: mem_clk +    type: from-master +    width: 1 +  - name: mem_rst +    type: from-master +    width: 1 +  - name: m_axi_awid +    type: to-master +    width: 2 +  - name: m_axi_awaddr +    type: to-master +    width: 64 +  - name: m_axi_awlen +    type: to-master +    width: 16 +  - name: m_axi_awsize +    type: to-master +    width: 6 +  - name: m_axi_awburst +    type: to-master +    width: 4 +  - name: m_axi_awlock +    type: to-master +    width: 2 +  - name: m_axi_awcache +    type: to-master +    width: 8 +  - name: m_axi_awprot +    type: to-master +    width: 6 +  - name: m_axi_awqos +    type: to-master +    width: 8 +  - name: m_axi_awregion +    type: to-master +    width: 8 +  - name: m_axi_awuser +    type: to-master +    width: 2 +  - name: m_axi_awvalid +    type: to-master +    width: 2 +  - name: m_axi_awready +    type: from-master +    width: 2 +  - name: m_axi_wdata +    type: to-master +    width: 128 +  - name: m_axi_wstrb +    type: to-master +    width: 16 +  - name: m_axi_wlast +    type: to-master +    width: 2 +  - name: m_axi_wuser +    type: to-master +    width: 2 +  - name: m_axi_wvalid +    type: to-master +    width: 2 +  - name: m_axi_wready +    type: from-master +    width: 2 +  - name: m_axi_bid +    type: from-master +    width: 2 +  - name: m_axi_bresp +    type: from-master +    width: 4 +  - name: m_axi_buser +    type: from-master +    width: 2 +  - name: m_axi_bvalid +    type: from-master +    width: 2 +  - name: m_axi_bready +    type: to-master +    width: 2 +  - name: m_axi_arid +    type: to-master +    width: 2 +  - name: m_axi_araddr +    type: to-master +    width: 64 +  - name: m_axi_arlen +    type: to-master +    width: 16 +  - name: m_axi_arsize +    type: to-master +    width: 6 +  - name: m_axi_arburst +    type: to-master +    width: 4 +  - name: m_axi_arlock +    type: to-master +    width: 2 +  - name: m_axi_arcache +    type: to-master +    width: 8 +  - name: m_axi_arprot +    type: to-master +    width: 6 +  - name: m_axi_arqos +    type: to-master +    width: 8 +  - name: m_axi_arregion +    type: to-master +    width: 8 +  - name: m_axi_aruser +    type: to-master +    width: 2 +  - name: m_axi_arvalid +    type: to-master +    width: 2 +  - name: m_axi_arready +    type: from-master +    width: 2 +  - name: m_axi_rid +    type: from-master +    width: 2 +  - name: m_axi_rdata +    type: from-master +    width: 128 +  - name: m_axi_rresp +    type: from-master +    width: 4 +  - name: m_axi_rlast +    type: from-master +    width: 2 +  - name: m_axi_ruser +    type: from-master +    width: 2 +  - name: m_axi_rvalid +    type: from-master +    width: 2 +  - name: m_axi_rready +    type: to-master +    width: 2 + +# A 4-port AXI4 memory-mapped interface with 64-bit data, and 4 GiB address +# space (32-bit). +axi4_mm_4x64_4g: +  type: master-slave +  ports: +  - name: mem_clk +    type: from-master +    width: 1 +  - name: mem_rst +    type: from-master +    width: 1 +  - name: m_axi_awid +    type: to-master +    width: 4 +  - name: m_axi_awaddr +    type: to-master +    width: 128 +  - name: m_axi_awlen +    type: to-master +    width: 32 +  - name: m_axi_awsize +    type: to-master +    width: 12 +  - name: m_axi_awburst +    type: to-master +    width: 8 +  - name: m_axi_awlock +    type: to-master +    width: 4 +  - name: m_axi_awcache +    type: to-master +    width: 16 +  - name: m_axi_awprot +    type: to-master +    width: 12 +  - name: m_axi_awqos +    type: to-master +    width: 16 +  - name: m_axi_awregion +    type: to-master +    width: 16 +  - name: m_axi_awuser +    type: to-master +    width: 4 +  - name: m_axi_awvalid +    type: to-master +    width: 4 +  - name: m_axi_awready +    type: from-master +    width: 4 +  - name: m_axi_wdata +    type: to-master +    width: 256 +  - name: m_axi_wstrb +    type: to-master +    width: 32 +  - name: m_axi_wlast +    type: to-master +    width: 4 +  - name: m_axi_wuser +    type: to-master +    width: 4 +  - name: m_axi_wvalid +    type: to-master +    width: 4 +  - name: m_axi_wready +    type: from-master +    width: 4 +  - name: m_axi_bid +    type: from-master +    width: 4 +  - name: m_axi_bresp +    type: from-master +    width: 8 +  - name: m_axi_buser +    type: from-master +    width: 4 +  - name: m_axi_bvalid +    type: from-master +    width: 4 +  - name: m_axi_bready +    type: to-master +    width: 4 +  - name: m_axi_arid +    type: to-master +    width: 4 +  - name: m_axi_araddr +    type: to-master +    width: 128 +  - name: m_axi_arlen +    type: to-master +    width: 32 +  - name: m_axi_arsize +    type: to-master +    width: 12 +  - name: m_axi_arburst +    type: to-master +    width: 8 +  - name: m_axi_arlock +    type: to-master +    width: 4 +  - name: m_axi_arcache +    type: to-master +    width: 16 +  - name: m_axi_arprot +    type: to-master +    width: 12 +  - name: m_axi_arqos +    type: to-master +    width: 16 +  - name: m_axi_arregion +    type: to-master +    width: 16 +  - name: m_axi_aruser +    type: to-master +    width: 4 +  - name: m_axi_arvalid +    type: to-master +    width: 4 +  - name: m_axi_arready +    type: from-master +    width: 4 +  - name: m_axi_rid +    type: from-master +    width: 4 +  - name: m_axi_rdata +    type: from-master +    width: 256 +  - name: m_axi_rresp +    type: from-master +    width: 8 +  - name: m_axi_rlast +    type: from-master +    width: 4 +  - name: m_axi_ruser +    type: from-master +    width: 4 +  - name: m_axi_rvalid +    type: from-master +    width: 4 +  - name: m_axi_rready +    type: to-master +    width: 4 diff --git a/host/include/uhd/rfnoc/dmafifo_block_control.hpp b/host/include/uhd/rfnoc/dmafifo_block_control.hpp new file mode 100644 index 000000000..ea15efef2 --- /dev/null +++ b/host/include/uhd/rfnoc/dmafifo_block_control.hpp @@ -0,0 +1,25 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_DMAFIFO_BLOCK_CONTROL_HPP +#define INCLUDED_LIBUHD_DMAFIFO_BLOCK_CONTROL_HPP + +#include <uhd/config.hpp> +#include <uhd/rfnoc/noc_block_base.hpp> + +namespace uhd { namespace rfnoc { + +/*! DMA FIFO Block Control Class + */ +class UHD_API dmafifo_block_control : public noc_block_base +{ +public: +    RFNOC_DECLARE_BLOCK(dmafifo_block_control) +}; + +}} // namespace uhd::rfnoc + +#endif /* INCLUDED_LIBUHD_DMAFIFO_BLOCK_CONTROL_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp index 446c37604..f69bafab9 100644 --- a/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp +++ b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp @@ -1,6 +1,7 @@  //  // Copyright 2015 Ettus Research LLC  // Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand  //  // SPDX-License-Identifier: GPL-3.0-or-later  // @@ -9,68 +10,48 @@  #define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP  #include <uhd/config.hpp> -#include <uhd/types/wb_iface.hpp> -#include <boost/shared_ptr.hpp>  #include <uhd/utils/noncopyable.hpp> - +#include <functional> +#include <memory>  class dma_fifo_core_3000 : uhd::noncopyable  {  public: -    typedef boost::shared_ptr<dma_fifo_core_3000> sptr; +    using sptr        = std::shared_ptr<dma_fifo_core_3000>; +    using poke32_fn_t = std::function<void(uint32_t, uint32_t)>; +    using peek32_fn_t = std::function<uint32_t(uint32_t)>; +      virtual ~dma_fifo_core_3000(void) = 0; -    /*! -     * Create a DMA FIFO controller using the given bus, settings and readback base -     * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA -     */ -    static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); +    //! Create a DMA FIFO controller for a specific channel +    static sptr make( +        poke32_fn_t&& poke_fn, peek32_fn_t&& peek_fn, const size_t fifo_index); -    /*! -     * Check if a DMA FIFO is instantiated in the FPGA -     */ -    static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); +    /************************************************************************** +     * API +     *************************************************************************/ +    virtual bool has_bist() const = 0; -    /*! -     * Flush the DMA FIFO. Will clear all contents. -     */ -    virtual bool flush(uint32_t timeout_ms = 2000) = 0; +    //! Return the fullness of the FIFO in bytes +    virtual uint64_t get_fifo_fullness() = 0; -    /*! -     * Resize and rebase the DMA FIFO. Will clear all contents. -     */ -    virtual void resize(const uint32_t base_addr, const uint32_t size) = 0; +    //! Get the transfer timeout value for the transfer in memory interface +    // clock cycles +    virtual uint16_t get_fifo_timeout() = 0; -    /*! -     * Get the (approx) number of bytes currently in the DMA FIFO -     */ -    virtual uint32_t get_bytes_occupied() = 0; +    //! Set the transfer timeout value for the transfer in memory interface +    // clock cycles +    virtual void set_fifo_timeout(const uint16_t timeout_cycles) = 0;      /*!       * Run the built-in-self-test routine for the DMA FIFO +     * +     * Returns an approximation of the RAM throughput.       */ -    virtual uint8_t run_bist(bool finite = true, uint32_t timeout_ms = 500) = 0; - -    /*! -     * Is extended BIST supported -     */ -    virtual bool ext_bist_supported() = 0; - -    /*! -     * Run the built-in-self-test routine for the DMA FIFO (extended BIST only) -     */ -    virtual uint8_t run_ext_bist( -        bool finite, -        uint32_t rx_samp_delay, -        uint32_t tx_pkt_delay, -        uint32_t sid, -        uint32_t timeout_ms = 500) = 0; - -    /*! -     * Get the throughput measured from the last invocation of the BIST (extended BIST only) -     */ -    virtual double get_bist_throughput() = 0; +    virtual double run_bist(const uint64_t num_bytes, const double timeout_s) = 0; +    //! Return the number of packats that have been transferred +    virtual uint32_t get_packet_count() = 0;  };  #endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */ diff --git a/host/lib/rfnoc/CMakeLists.txt b/host/lib/rfnoc/CMakeLists.txt index 5f5838a29..ae531275f 100644 --- a/host/lib/rfnoc/CMakeLists.txt +++ b/host/lib/rfnoc/CMakeLists.txt @@ -42,6 +42,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ddc_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/duc_block_control.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/dmafifo_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/null_block_control.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_control_impl.cpp  ) diff --git a/host/lib/rfnoc/dmafifo_block_control.cpp b/host/lib/rfnoc/dmafifo_block_control.cpp new file mode 100644 index 000000000..c34746a28 --- /dev/null +++ b/host/lib/rfnoc/dmafifo_block_control.cpp @@ -0,0 +1,71 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#include <uhd/rfnoc/dmafifo_block_control.hpp> +#include <uhd/rfnoc/registry.hpp> +#include <uhdlib/usrp/cores/dma_fifo_core_3000.hpp> +#include <boost/format.hpp> +#include <mutex> + +using namespace uhd; +using namespace uhd::rfnoc; + +namespace { + +//! Default FIFO depth per channel: 32 MiB +constexpr uint32_t DEFAULT_SIZE   = 32 * 1024 * 1024; +constexpr uint64_t BYTES_PER_BIST = 8000000; +constexpr double BIST_TIMEOUT     = 0.5; // s +//! Address space between FIFO controls +const uint32_t REG_OFFSET = 128; + +} // namespace + +class dmafifo_block_control_impl : public dmafifo_block_control +{ +public: +    RFNOC_BLOCK_CONSTRUCTOR(dmafifo_block_control) +    { +        UHD_ASSERT_THROW(get_num_input_ports() == get_num_output_ports()); +        set_action_forwarding_policy(forwarding_policy_t::ONE_TO_ONE); +        set_prop_forwarding_policy(forwarding_policy_t::ONE_TO_ONE); +        // Now init DMA/DRAM control +        _fifo_cores.reserve(get_num_input_ports()); +        for (size_t i = 0; i < get_num_input_ports(); i++) { +            _fifo_cores.push_back(dma_fifo_core_3000::make( +                [this, i](const uint32_t addr, const uint32_t data) { +                    regs().poke32(addr + i * REG_OFFSET, data); +                }, +                [this, i]( +                    const uint32_t addr) { return regs().peek32(addr + i * REG_OFFSET); }, +                i)); +            RFNOC_LOG_DEBUG("Initialized FIFO core " << i << "."); +            if (_fifo_cores.back()->has_bist()) { +                RFNOC_LOG_DEBUG("Running BIST..."); +                const double throughput = +                    _fifo_cores.back()->run_bist(BYTES_PER_BIST, BIST_TIMEOUT); +                RFNOC_LOG_INFO( +                    boost::format("BIST passed (Estimated Minimum Throughput: %.0f MB/s)") +                    % (throughput / 1e6)); +            } else { +                RFNOC_LOG_DEBUG("Channel " << i << " does not support BIST, skipping."); +            } +        } +    } + +    uint32_t get_packet_count(const size_t chan) +    { +        UHD_ASSERT_THROW(chan < _fifo_cores.size()); +        return _fifo_cores.at(chan)->get_packet_count(); +    } + +private: +    //! One FIFO core object per FIFO +    std::vector<dma_fifo_core_3000::sptr> _fifo_cores; +}; + +UHD_RFNOC_BLOCK_REGISTER_DIRECT( +    dmafifo_block_control, 0xF1F00000, "DmaFIFO", CLOCK_KEY_GRAPH, "bus_clk") diff --git a/host/lib/rfnoc/node.cpp b/host/lib/rfnoc/node.cpp index 90f0a0a91..23d5a340d 100644 --- a/host/lib/rfnoc/node.cpp +++ b/host/lib/rfnoc/node.cpp @@ -446,7 +446,7 @@ void node_t::clean_props()      prop_accessor_t prop_accessor{};      for (const auto& type_prop_pair : _props) {          for (const auto& prop : type_prop_pair.second) { -            if (prop->is_dirty() && _clean_cb_registry.count(prop)) { +            if (prop->is_valid() && prop->is_dirty() && _clean_cb_registry.count(prop)) {                  _clean_cb_registry.at(prop)();              }              prop_accessor.mark_clean(*prop); diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp index db36a336b..382006b67 100644 --- a/host/lib/usrp/cores/dma_fifo_core_3000.cpp +++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp @@ -1,426 +1,322 @@  //  // Copyright 2015 Ettus Research LLC  // Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand  //  // SPDX-License-Identifier: GPL-3.0-or-later  //  #include <uhd/exception.hpp> -#include <uhd/utils/soft_register.hpp>  #include <uhd/utils/log.hpp>  #include <uhdlib/usrp/cores/dma_fifo_core_3000.hpp> -#include <boost/format.hpp>  #include <chrono> +#include <mutex>  #include <thread>  using namespace uhd; +using namespace std::chrono_literals; + +namespace { + +//! Info register: [0]: Indicates presence of BIST [31:16]: Magic word +const uint32_t REG_FIFO_INFO = 0x0000; +const uint16_t FIFO_MAGIC    = 0xF1F0; + +//! Mem size register: [RAM Word Size | Address size] +const uint32_t REG_FIFO_MEM_SIZE      = 0x0008; +const uint32_t REG_FIFO_TIMEOUT       = 0x000C; +const uint32_t REG_FIFO_FULLNESS      = 0x0010; +const uint32_t REG_FIFO_ADDR_BASE     = 0x0018; +const uint32_t REG_FIFO_ADDR_MASK     = 0x0020; +const uint32_t REG_FIFO_PACKET_CNT    = 0x0028; +const uint32_t REG_BIST_CTRL          = 0x0030; +const uint32_t REG_BIST_CLK_RATE      = 0x0034; +const uint32_t REG_BIST_NUM_BYTES     = 0x0038; +const uint32_t REG_BIST_TX_BYTE_COUNT = 0x0040; +const uint32_t REG_BIST_RX_BYTE_COUNT = 0x0048; +const uint32_t REG_BIST_ERROR_COUNT   = 0x0050; +const uint32_t REG_BIST_CYCLE_COUNT   = 0x0058; + +//! Read FIFO info register, check for magic, return "has bist" flag all in one +// go +bool unpack_fifo_info(const uint32_t fifo_info) +{ +    if (fifo_info >> 16 != FIFO_MAGIC) { +        throw uhd::runtime_error("DMA FIFO: Incorrect magic value returned!"); +    } + +    return bool(fifo_info & 0x1); +} -#define SR_DRAM_BIST_BASE 16 +} // namespace -dma_fifo_core_3000::~dma_fifo_core_3000(void) { +dma_fifo_core_3000::~dma_fifo_core_3000(void) +{      /* NOP */  }  class dma_fifo_core_3000_impl : public dma_fifo_core_3000  { -protected: -    class rb_addr_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(ADDR, /*width*/ 3, /*shift*/ 0);  //[2:0] - -        static const uint32_t RB_FIFO_STATUS     = 0; -        static const uint32_t RB_BIST_STATUS     = 1; -        static const uint32_t RB_BIST_XFER_CNT   = 2; -        static const uint32_t RB_BIST_CYC_CNT    = 3; -        static const uint32_t RB_BUS_CLK_RATE    = 4; -        static const uint32_t RB_OUT_PKT_CNT     = 5; - -        rb_addr_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 0) -        { -            //Initial values -            set(ADDR, RB_FIFO_STATUS); -        } -    }; - -    class fifo_ctrl_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(CLEAR_FIFO,           /*width*/  1, /*shift*/  0);  //[0] -        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_EN,       /*width*/  1, /*shift*/  1);  //[1] -        UHD_DEFINE_SOFT_REG_FIELD(FLUSH_PKTS,           /*width*/  1, /*shift*/  2);  //[2] -        UHD_DEFINE_SOFT_REG_FIELD(BURST_TIMEOUT,        /*width*/ 12, /*shift*/  4);  //[15:4] -        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_THRESH,   /*width*/ 16, /*shift*/ 16);  //[31:16] - -        fifo_ctrl_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 4) -        { -            //Initial values -            set(CLEAR_FIFO, 0); -            set(RD_SUPPRESS_EN, 0); -            set(FLUSH_PKTS, 0); -            set(BURST_TIMEOUT, 256); -            set(RD_SUPPRESS_THRESH, 0); -        } -    }; - -    class base_addr_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(BASE_ADDR, /*width*/ 30, /*shift*/ 0);  //[29:0] - -        base_addr_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 8) -        { -            //Initial values -            set(BASE_ADDR, 0x00000000); -        } -    }; - -    class addr_mask_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(ADDR_MASK, /*width*/ 30, /*shift*/ 0);  //[29:0] - -        addr_mask_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 12) -        { -            //Initial values -            set(ADDR_MASK, 0xFF000000); -        } -    }; - -    class bist_ctrl_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(GO,               /*width*/ 1, /*shift*/ 0);  //[0] -        UHD_DEFINE_SOFT_REG_FIELD(CONTINUOUS_MODE,  /*width*/ 1, /*shift*/ 1);  //[1] -        UHD_DEFINE_SOFT_REG_FIELD(TEST_PATT,        /*width*/ 2, /*shift*/ 4);  //[5:4] - -        static const uint32_t TEST_PATT_ZERO_ONE     = 0; -        static const uint32_t TEST_PATT_CHECKERBOARD = 1; -        static const uint32_t TEST_PATT_COUNT        = 2; -        static const uint32_t TEST_PATT_COUNT_INV    = 3; - -        bist_ctrl_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 16) -        { -            //Initial values -            set(GO, 0); -            set(CONTINUOUS_MODE, 0); -            set(TEST_PATT, TEST_PATT_ZERO_ONE); -        } -    }; +public: +    /************************************************************************** +     * Structors +     *************************************************************************/ +    dma_fifo_core_3000_impl( +        poke32_fn_t&& poke_fn, peek32_fn_t&& peek_fn, const size_t fifo_index) +        : _fifo_index(fifo_index) +        , poke32(std::move(poke_fn)) +        , peek32(std::move(peek_fn)) +        , _has_bist(unpack_fifo_info(peek32(REG_FIFO_INFO))) +        , _mem_size(peek32(REG_FIFO_MEM_SIZE)) +        , _bist_clk_rate(_has_bist ? peek32(REG_BIST_CLK_RATE) : 0.0) +    { +        UHD_LOG_DEBUG("DMA FIFO", +            "Initializing FIFO core " +                << _fifo_index << ": RAM Word Width: " << _mem_size.word_size << " bits, " +                << "address width: " << _mem_size.addr_size +                << " bits, base address: " << this->get_addr_base() +                << ", FIFO size: " << (uint64_t(get_addr_mask()) + 1) / 1024 / 1024 +                << " MiB, has BIST: " << (_has_bist ? "Yes" : "No") +                << ", BIST clock rate: " << (_bist_clk_rate / 1e6) +                << " MHz, Initial FIFO fullness: " +                << dma_fifo_core_3000_impl::get_fifo_fullness() << ", FIFO timeout: " +                << dma_fifo_core_3000_impl::get_fifo_timeout() << " cycles"); +    } -    class bist_cfg_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKTS,         /*width*/ 18, /*shift*/ 0);  //[17:0] -        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKT_SIZE,     /*width*/ 13, /*shift*/ 18); //[30:18] -        UHD_DEFINE_SOFT_REG_FIELD(PKT_SIZE_RAMP,    /*width*/ 1,  /*shift*/ 31); //[31] +    virtual ~dma_fifo_core_3000_impl() {} -        bist_cfg_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 20) -        { -            //Initial values -            set(MAX_PKTS, 0); -            set(MAX_PKT_SIZE, 0); -            set(PKT_SIZE_RAMP, 0); -        } -    }; +    /************************************************************************** +     * API +     *************************************************************************/ +    bool has_bist() const +    { +        return _has_bist; +    } -    class bist_delay_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(TX_PKT_DELAY,     /*width*/ 16, /*shift*/ 0);  //[15:0] -        UHD_DEFINE_SOFT_REG_FIELD(RX_SAMP_DELAY,    /*width*/  8, /*shift*/ 16); //[23:16] +    uint64_t get_addr_size() const +    { +        return _mem_size.addr_size; +    } -        bist_delay_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 24) -        { -            //Initial values -            set(TX_PKT_DELAY, 0); -            set(RX_SAMP_DELAY, 0); -        } -    }; +    // TODO: read suppress API -    class bist_sid_reg_t : public soft_reg32_wo_t { -    public: -        UHD_DEFINE_SOFT_REG_FIELD(SID,     /*width*/ 32, /*shift*/ 0);  //[31:0] +    // fullness in bytes +    uint64_t get_fifo_fullness() +    { +        return peek64(REG_FIFO_FULLNESS); +    } -        bist_sid_reg_t(uint32_t base): -            soft_reg32_wo_t(base + 28) -        { -            //Initial values -            set(SID, 0); -        } -    }; +    uint16_t get_fifo_timeout() +    { +        return peek32(REG_FIFO_TIMEOUT) & 0xFFF; +    } -public: -    class fifo_readback { -    public: -        fifo_readback(wb_iface::sptr iface,  const size_t base, const size_t rb_addr) : -            _iface(iface), _addr_reg(base), _rb_addr(rb_addr) -        { -            _addr_reg.initialize(*iface, true); -        } +    void set_fifo_timeout(const uint16_t timeout_cycles) +    { +        UHD_ASSERT_THROW(timeout_cycles <= 0xFFF); +        poke32(timeout_cycles, REG_FIFO_TIMEOUT); +    } -        bool is_fifo_instantiated() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); -            return _iface->peek32(_rb_addr) & 0x80000000; -        } +    uint64_t get_addr_base() +    { +        return peek64(REG_FIFO_ADDR_BASE); +    } -        uint32_t get_occupied_cnt() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); -            return _iface->peek32(_rb_addr) & 0x7FFFFFF; -        } +    uint64_t get_addr_mask() +    { +        return peek64(REG_FIFO_ADDR_MASK); +    } -        uint32_t get_out_pkt_cnt() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_OUT_PKT_CNT); -            return _iface->peek32(_rb_addr); -        } +    void set_addr_mask(uint64_t addr_mask) +    { +        return poke64(REG_FIFO_ADDR_MASK, addr_mask); +    } -        struct bist_status_t { -            bool running; -            bool finished; -            uint8_t error; -        }; - -        bist_status_t get_bist_status() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); -            uint32_t st32 = _iface->peek32(_rb_addr) & 0xF; -            bist_status_t status; -            status.running = st32 & 0x1; -            status.finished = st32 & 0x2; -            status.error = static_cast<uint8_t>((st32>>2) & 0x3); -            return status; -        } +    void set_addr_base(const uint64_t base_addr) +    { +        // FIXME verify we're within bounds +        poke64(REG_FIFO_ADDR_BASE, base_addr); +    } -        bool is_ext_bist_supported() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); -            return _iface->peek32(_rb_addr) & 0x80000000; -        } +    uint32_t get_packet_count() +    { +        return peek32(REG_FIFO_PACKET_CNT); +    } -        double get_xfer_ratio() { -            boost::lock_guard<boost::mutex> lock(_mutex); -            uint32_t xfer_cnt = 0, cyc_cnt = 0; -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_XFER_CNT); -            xfer_cnt = _iface->peek32(_rb_addr); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_CYC_CNT); -            cyc_cnt = _iface->peek32(_rb_addr); -            return (static_cast<double>(xfer_cnt)/cyc_cnt); -        } +    /***** BIST controls *****************************************************/ +    bool get_bist_running() +    { +        return bool(peek32(REG_BIST_CTRL) & (1 << 4)); +    } -        double get_bus_clk_rate() { -            uint32_t bus_clk_rate = 0; -            boost::lock_guard<boost::mutex> lock(_mutex); -            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BUS_CLK_RATE); -            bus_clk_rate = _iface->peek32(_rb_addr); -            return (static_cast<double>(bus_clk_rate)); -        } +    void bist_clear_counters() +    { +        // strobe +        poke32(REG_BIST_CTRL, 1 << 2); +    } -    private: -        wb_iface::sptr  _iface; -        rb_addr_reg_t   _addr_reg; -        const size_t    _rb_addr; -        boost::mutex    _mutex; -    }; +    uint64_t get_bist_num_bytes() +    { +        return peek64(REG_BIST_NUM_BYTES); +    } -public: -    dma_fifo_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): -        _iface(iface), _fifo_readback(iface, base, readback), -        _fifo_ctrl_reg(base), _base_addr_reg(base), _addr_mask_reg(base), -        _bist_ctrl_reg(base), _bist_cfg_reg(base), _bist_delay_reg(base), _bist_sid_reg(base) +    void set_bist_num_bytes(uint64_t num_bytes)      { -        _fifo_ctrl_reg.initialize(*iface, true); -        _base_addr_reg.initialize(*iface, true); -        _addr_mask_reg.initialize(*iface, true); -        _bist_ctrl_reg.initialize(*iface, true); -        _bist_cfg_reg.initialize(*iface, true); -        _has_ext_bist = _fifo_readback.is_ext_bist_supported(); -        if (_has_ext_bist) { -            _bist_delay_reg.initialize(*iface, true); -            _bist_sid_reg.initialize(*iface, true); -        } +        poke64(REG_BIST_NUM_BYTES, num_bytes);      } -    virtual ~dma_fifo_core_3000_impl() { -        //Clear the FIFO and hold it in that state -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::FLUSH_PKTS, 1); +    uint64_t get_bist_tx_byte_count() +    { +        return peek64(REG_BIST_TX_BYTE_COUNT);      } -    virtual bool flush(uint32_t timeout_ms = 2000) { -        //Flush the FIFO and wait for packets to drain -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::FLUSH_PKTS, 1); -        bool success = _wait_for_fifo_empty(timeout_ms); -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::FLUSH_PKTS, 0); -        return success; +    uint64_t get_bist_rx_byte_count() +    { +        return peek64(REG_BIST_RX_BYTE_COUNT);      } -    virtual void resize(const uint32_t base_addr, const uint32_t size) { -        //Validate parameters -        if (size < 8192) throw uhd::runtime_error("DMA FIFO must be larger than 8KiB"); -        uint32_t size_mask = size - 1; -        if (size & size_mask) throw uhd::runtime_error("DMA FIFO size must be a power of 2"); - -        //Flush the FIFO and wait for packets to drain -        flush(); -        //Clear the FIFO and hold it in that state -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); -        //Write base address and mask -        _base_addr_reg.write(base_addr_reg_t::BASE_ADDR, base_addr); -        _addr_mask_reg.write(addr_mask_reg_t::ADDR_MASK, ~size_mask); -        //Re-arm the FIFO -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0); +    uint64_t get_bist_error_count() +    { +        return peek64(REG_BIST_ERROR_COUNT);      } -    virtual uint32_t get_bytes_occupied() { -        return _fifo_readback.get_occupied_cnt() * 8; +    uint64_t get_bist_cycle_count() +    { +        return peek64(REG_BIST_CYCLE_COUNT);      } -    virtual bool ext_bist_supported() { -        return _fifo_readback.is_ext_bist_supported(); +    void start_bist(const bool cont) +    { +        // strobe +        poke32(REG_BIST_CTRL, 1 | (cont ? (1 << 3) : 0));      } -    virtual uint8_t run_bist(bool finite = true, uint32_t timeout_ms = 500) { -        return run_ext_bist(finite, 0, 0, 0, timeout_ms); +    void stop_bist() +    { +        // strobe +        poke32(REG_BIST_CTRL, 1 << 2);      } -    virtual uint8_t run_ext_bist( -        bool finite, -        uint32_t rx_samp_delay, -        uint32_t tx_pkt_delay, -        uint32_t sid, -        uint32_t timeout_ms = 500 -    ) { -        boost::lock_guard<boost::mutex> lock(_mutex); - -        //Stop previous BIST and wait (if running) -        _wait_for_bist_done(timeout_ms, true); - -        //Flush the FIFO and wait for packets to drain -        flush(); -        //Clear the FIFO and reset the BIST engine -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); -        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0); - -        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKTS, (2^18)-1); -        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKT_SIZE, 8000); -        _bist_cfg_reg.set(bist_cfg_reg_t::PKT_SIZE_RAMP, 0); -        _bist_cfg_reg.flush(); - -        if (_has_ext_bist) { -            _bist_delay_reg.set(bist_delay_reg_t::RX_SAMP_DELAY, rx_samp_delay); -            _bist_delay_reg.set(bist_delay_reg_t::TX_PKT_DELAY, tx_pkt_delay); -            _bist_delay_reg.flush(); - -            _bist_sid_reg.write(bist_sid_reg_t::SID, sid); -        } else { -            if (rx_samp_delay != 0 || tx_pkt_delay != 0 || sid != 0) { -                throw uhd::not_implemented_error( -                    "dma_fifo_core_3000: Runtime delay and SID support only available on FPGA images with extended BIST enabled"); +    double run_bist(const uint64_t num_bytes, const double timeout_s) +    { +        // The number of cycles it will take to transfer all the BIST data if +        // there is a transfer on every clock cycle (this is the minimum time it +        // will take to execute the BIST): +        const uint64_t min_cycles = (num_bytes / (_mem_size.word_size / 8)) +                                    + (num_bytes % (_mem_size.word_size / 8) ? 1 : 0); +        const auto min_duration = +            std::chrono::milliseconds(int64_t(min_cycles / _bist_clk_rate * 1e3)); +        // The user could specify inconsistent values where the timeout is +        // smaller than the time it actually takes to execute the BIST, but we +        // simply interpret that as "as soon as possible" and don't throw an +        // error. +        const auto end_time = std::chrono::steady_clock::now() +                              + std::chrono::milliseconds(int64_t(timeout_s * 1000)); +        bist_clear_counters(); +        set_bist_num_bytes(num_bytes); +        // Start BIST in non-continuous mode +        start_bist(false); +        // Now wait at least for the minimum time it takes to execute the BIST +        std::this_thread::sleep_for(min_duration); +        // Now poll for finished until timeout +        while (get_bist_running()) { +            if (std::chrono::steady_clock::now() > end_time) { +                UHD_LOG_ERROR("DMA FIFO", "Timeout during BIST!"); +                UHD_LOG_DEBUG("DMA FIFO", +                    "TX bytes transferred: " +                        << get_bist_tx_byte_count() +                        << " RX bytes transferred: " << get_bist_rx_byte_count() +                        << " Cycle count: " << get_bist_cycle_count()); +                throw uhd::runtime_error("[DMA FIFO] Timeout during BIST!");              } +            std::this_thread::sleep_for(5ms);          } - -        _bist_ctrl_reg.set(bist_ctrl_reg_t::TEST_PATT, bist_ctrl_reg_t::TEST_PATT_COUNT); -        _bist_ctrl_reg.set(bist_ctrl_reg_t::CONTINUOUS_MODE, finite ? 0 : 1); -        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 1); - -        if (!finite) { -            std::this_thread::sleep_for(std::chrono::milliseconds(timeout_ms)); +        // Now the BIST is complete, we check all the transfers were accurate +        // and there were no errors +        bool error                = false; +        const uint64_t tx_bytes   = get_bist_tx_byte_count(); +        const uint64_t rx_bytes   = get_bist_rx_byte_count(); +        const uint64_t num_errors = get_bist_error_count(); +        const uint64_t num_cycles = get_bist_cycle_count(); +        const double bist_time    = double(num_cycles) / _bist_clk_rate; +        if (tx_bytes != num_bytes) { +            UHD_LOG_ERROR("DMA FIFO", +                "BIST Error: Incorrect number of TX bytes! " +                "Transmitted: " +                    << tx_bytes << " Expected: " << num_bytes); +            error = true;          } - -        _wait_for_bist_done(timeout_ms, !finite); -        if (!_fifo_readback.get_bist_status().finished) { -            throw uhd::runtime_error("dma_fifo_core_3000: DRAM BIST state machine is in a bad state."); +        if (rx_bytes != num_bytes) { +            UHD_LOG_ERROR("DMA FIFO", +                "BIST Error: Incorrect number of RX bytes! " +                "Received: " +                    << rx_bytes << " Expected: " << num_bytes); +            error = true; +        } +        if (num_errors != 0) { +            UHD_LOG_ERROR("DMA FIFO", "BIST Error: Error count is " << num_errors); +            error = true;          } +        UHD_LOG_DEBUG("DMA FIFO", +            "BIST: Cycles elapsed: " << num_cycles << " Best Case: " << min_cycles); +        if (error) { +            throw uhd::runtime_error("[DMA FIFO] Bist failed!"); +        } +        bist_clear_counters(); -        return _fifo_readback.get_bist_status().error; +        // Return throughput in byte/s +        return num_bytes / bist_time;      } -    virtual double get_bist_throughput() { -        if (_has_ext_bist) { -            _wait_for_bist_done(1000); -            static const double BYTES_PER_CYC = 8; -            double bus_clk_rate = _fifo_readback.get_bus_clk_rate(); -            return _fifo_readback.get_xfer_ratio() * bus_clk_rate * BYTES_PER_CYC; -        } else { -            throw uhd::not_implemented_error( -                "dma_fifo_core_3000: Throughput counter only available on FPGA images with extended BIST enabled"); -        } +private: +    const size_t _fifo_index; +    poke32_fn_t poke32; +    peek32_fn_t peek32; + +    //! Convenience function to do two consecutive peek32's +    uint64_t peek64(const uint32_t addr) +    { +        const uint32_t lo = peek32(addr); +        const uint32_t hi = peek32(addr + 4); +        return static_cast<uint64_t>(lo) | (static_cast<uint64_t>(hi) << 32);      } -private: -    bool _wait_for_fifo_empty(uint32_t timeout_ms = 2000) { -        auto is_data_streaming = [this](uint32_t time_ms) -> bool { -            auto old_cnts = _fifo_readback.get_out_pkt_cnt(); -            std::this_thread::sleep_for(std::chrono::milliseconds(time_ms)); -            auto new_cnts = _fifo_readback.get_out_pkt_cnt(); -            return (new_cnts != old_cnts); -        }; - -        // Check for activity. Default timeout is 2s. For a 200MHz bus_clk that's 3200MB of data. -        // We use a 10ms window to check for activity which detects a stream with approx -        // 100 packets per second. All timeouts are approximate. No need to make a kernel -        // call to get system time. -        constexpr uint32_t CHK_WINDOW_MS = 10; -        for (uint32_t i = 0; i < timeout_ms/CHK_WINDOW_MS; i++) { -            if (not is_data_streaming(CHK_WINDOW_MS)) { -                return true; -            } -        } -        return false; +    //! Convenience function to do two consecutive poke32's +    void poke64(const uint32_t addr, const uint64_t data) +    { +        const uint32_t lo = static_cast<uint32_t>(data & 0xFFFFFFFF); +        const uint32_t hi = static_cast<uint32_t>((data >> 32) & 0xFFFFFFFF); +        poke32(addr, lo); +        poke32(addr + 4, hi);      } -    bool _wait_for_bist_done(uint32_t timeout_ms, bool force_stop = false) { -        // Stop the BIST if requested and it is running -        if (_fifo_readback.get_bist_status().running) { -            if (force_stop) { -                _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0); -            } -        } else { -            return true; +    //! True if we can do BISTs with this FIFO +    const bool _has_bist; +    struct mem_size_t +    { +        mem_size_t(const uint32_t mem_size) +            : word_size((mem_size >> 16) & 0xFFFF), addr_size(mem_size & 0xFFFF) +        {          } -        // BIST is still running. Wait for it to finish -        // Timeout is approximate. No need to make a kernel call to get system time. -        for (uint32_t i = 0; i < timeout_ms; i++) { -            std::this_thread::sleep_for(std::chrono::milliseconds(1)); -            if (not _fifo_readback.get_bist_status().running) { -                return true; -            } -        } -        return false; -    } +        //! RAM word width (e.g., 64) +        const uint16_t word_size; +        //! Width of the max available address size (in bits) +        const uint16_t addr_size; +    }; +    //! Cache the contents of the MEM_SIZE register +    const mem_size_t _mem_size; -private: -    wb_iface::sptr  _iface; -    boost::mutex    _mutex; -    bool            _has_ext_bist; - -    fifo_readback       _fifo_readback; -    fifo_ctrl_reg_t     _fifo_ctrl_reg; -    base_addr_reg_t     _base_addr_reg; -    addr_mask_reg_t     _addr_mask_reg; -    bist_ctrl_reg_t     _bist_ctrl_reg; -    bist_cfg_reg_t      _bist_cfg_reg; -    bist_delay_reg_t    _bist_delay_reg; -    bist_sid_reg_t      _bist_sid_reg; +    //! Clock rate of the BIST circuitry in Hz +    const double _bist_clk_rate;  };  // -// Static make function +// Factory  // -dma_fifo_core_3000::sptr dma_fifo_core_3000::make(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) -{ -    if (check(iface, set_base, rb_addr)) { -        return sptr(new dma_fifo_core_3000_impl(iface, set_base, rb_addr)); -    } else { -        throw uhd::runtime_error(""); -    } -} - -bool dma_fifo_core_3000::check(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +dma_fifo_core_3000::sptr dma_fifo_core_3000::make( +    poke32_fn_t&& poke_fn, peek32_fn_t&& peek_fn, const size_t fifo_index)  { -    dma_fifo_core_3000_impl::fifo_readback fifo_rb(iface, set_base, rb_addr); -    return fifo_rb.is_fifo_instantiated(); +    return std::make_shared<dma_fifo_core_3000_impl>( +        std::move(poke_fn), std::move(peek_fn), fifo_index);  } | 
