Program Listing for File tohost_buffer.hpp

Return to documentation for file (tohost_buffer.hpp)

#ifndef TOHOST_BUFFER_H_
#define TOHOST_BUFFER_H_

#include <cstddef>
#include <cstdint>
#include <thread>
#include <span>
#include <mutex>
#include <shared_mutex>
#include <cstdio>
#include <condition_variable>

#include "device.hpp"
#include "cmem_buffer.hpp"
#include "block.hpp"


#if REGMAP_VERSION < 0x500
    #define IRQ_WRAP_AROUND_FROM_HOST 0
    #define IRQ_WRAP_AROUND_TO_HOST 1
    #define IRQ_DATA_AVAILABLE 2
    #define IRQ_FIFO_FULL_FROM_HOST 3
    #define IRQ_BUSY_CHANGE_TO_HOST 6
    #define IRQ_FIFO_FULL_TO_HOST 7
#else
    #define IRQ_DATA_AVAILABLE 0
    #define IRQ_XOFF_TO_HOST 5
    #define IRQ_BUSY_CHANGE_TO_HOST 6
    #define IRQ_FIFO_FULL_TO_HOST 7
#endif

#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))


class ToHostBuffer
{
    public:
        ToHostBuffer(int dmaid, std::shared_ptr<Device> d);

        //Interrupts
        void irq_data_enable() {m_device->irq_enable(m_irq_on_data);}

        void irq_data_disable(){m_device->irq_disable(m_irq_on_data);}

        void irq_busy_enable() {m_device->irq_enable(IRQ_BUSY_CHANGE_TO_HOST);}

        void irq_busy_disable(){m_device->irq_disable(IRQ_BUSY_CHANGE_TO_HOST);}
        std::shared_ptr<Device> get_device(){return m_device;}

        int get_dmaid() const {return m_dmaid;}

        std::vector<Elink> get_elinks(){return m_device->read_enabled_elinks(m_dmaid);}

        std::vector<Elink> get_elinks_of_type(elink_type_t t){return m_device->get_enabled_elinks_of_type(m_dmaid, t);};

        std::vector<std::vector<Elink>> split_elinks_of_type(elink_type_t t, size_t n);

        //DMA buffer operations
        virtual void allocate_buffer(size_t size,
                            const std::string& name,
                            bool vmem, bool free_previous_cmem=true) = 0;

        virtual void     dma_start_continuous()                  = 0; //start_circular_dma

        virtual void     dma_wait_for_data_irq()                 = 0; //wait_data_available

        virtual size_t   dma_bytes_available()                   = 0; //bytes_available

        virtual size_t   dma_bytes_available_nowrap()            = 0; //bytes_available_to_read

        virtual bool     dma_is_full()                           = 0;

        virtual uint64_t dma_get_write_ptr()                     = 0;

        virtual uint64_t dma_get_read_ptr()                      = 0;

        virtual void     dma_set_read_ptr_vaddr(uint64_t v_addr) = 0;

        virtual void     dma_set_read_ptr_paddr(uint64_t p_addr) = 0;

        virtual uint64_t dma_get_read_offset()                   = 0;

        virtual uint64_t dma_get_write_offset()                  = 0;

        virtual void     dma_advance_read_ptr(size_t bytes)      = 0;

        uint64_t         dma_get_vaddr(){return m_buffer->vaddr;}

        size_t           dma_get_size(){return m_buffer->size;}
        uint32_t         reader_register(bool zc_flag);

        bool             reader_is_data_available(uint32_t reader_id);

        size_t           reader_get_available_bytes(uint32_t reader_id);

        std::span<Block> reader_get_available_blocks(uint32_t reader_id);

        void             reader_advance_read_ptr(uint32_t reader_id,  uint32_t read_bytes, uint32_t sent_bytes);
        bool             has_zero_copy_reader() const {return m_has_zero_copy_reader;}

        void             set_zero_copy_reader(){m_has_zero_copy_reader = true;}
        void             stop(){m_stop_flag = true;}

        bool             is_stopped() const {return m_stop_flag;}

        uint32_t dma_get_free_MB();

        void dma_increase_irq_counter(){++m_irq_counter;}

        uint64_t dma_get_irq_counter() const {return m_irq_counter;}
    protected:
        int m_dmaid;
        std::shared_ptr<Device> m_device;
        unsigned int m_block_size;
        int m_irq_on_data; //IRQ_DATA_AVAILABLE + m_dmaid;
        uint64_t m_irq_counter;
        size_t m_size;
        bool m_stop_flag; //stop flag for readers
        bool m_has_zero_copy_reader;
        size_t m_min_bytes_sent;

        std::mutex m_mutex;
        std::unique_ptr<DmaBuffer> m_buffer;

        //support for multiple ToHostBufferReader
        std::vector<uint64_t> m_bytes_read; //read from DMA buffer
        std::vector<uint64_t> m_bytes_sent; //different from read only for zc mode

        size_t dma_compute_bytes_to_read(uint64_t rd_ptr, uint64_t wr_ptr, bool even);
};


inline size_t ToHostBuffer::dma_compute_bytes_to_read(uint64_t rd_ptr, uint64_t wr_ptr, bool even)
{
    if( wr_ptr > rd_ptr ) {
        return wr_ptr - rd_ptr;
    }
    else if( wr_ptr < rd_ptr ) {
        return m_buffer->size + wr_ptr -rd_ptr;
    }
    else { // dma_ptr == buf->pc_ptr
        if( even ) {
            return 0;         // Buffer empty
        }
        else {
            return m_buffer->size; // Buffer full
        }
    }
}


class FlxToHostBuffer : public ToHostBuffer {

    public:
        FlxToHostBuffer(int m_dmaid, std::shared_ptr<Device> d);
        ~FlxToHostBuffer();


        void allocate_buffer(size_t size,
                            const std::string& name,
                            bool vmem, bool free_previous_cmem=false) override;

        void     dma_start_continuous()                  override;
        void     dma_wait_for_data_irq()                 override;
        size_t   dma_bytes_available()                   override;
        //uint32_t dma_get_free_MB()                       override;
        size_t   dma_bytes_available_nowrap()            override;
        bool     dma_is_full()                           override;
        uint64_t dma_get_write_ptr()                     override;
        uint64_t dma_get_read_ptr()                      override;
        void     dma_set_read_ptr_vaddr(uint64_t v_addr) override;
        void     dma_set_read_ptr_paddr(uint64_t p_addr) override;
        uint64_t dma_get_read_offset()                   override;
        uint64_t dma_get_write_offset()                  override;
        void     dma_advance_read_ptr(size_t bytes)      override;
};


class FileToHostBuffer : public ToHostBuffer {

    public:
        FileToHostBuffer(int m_dmaid, std::shared_ptr<Device> d,
            std::string& filename, unsigned int block_rate, bool repeat);
        ~FileToHostBuffer();
        void allocate_buffer(size_t size,
                            const std::string& name,
                            bool vmem, bool free_previous_cmem=true) override;

        void     dma_start_continuous()                  override;
        void     dma_wait_for_data_irq()                 override;
        size_t   dma_bytes_available()                   override;
        //uint32_t dma_get_free_MB()                       override;
        size_t   dma_bytes_available_nowrap()            override;
        bool     dma_is_full()                           override;
        uint64_t dma_get_write_ptr()                     override;
        uint64_t dma_get_read_ptr()                      override;
        void     dma_set_read_ptr_vaddr(uint64_t v_addr) override;
        void     dma_set_read_ptr_paddr(uint64_t p_addr) override;
        uint64_t dma_get_read_offset()                   override;
        uint64_t dma_get_write_offset()                  override;
        void     dma_advance_read_ptr(size_t bytes)      override;

    private:
        void    set_read_ptr_paddr(uint64_t p_addr);
        void    write_in_dma_buffer();
        size_t  limit_block_rate(size_t max_blocks);
        size_t  blocks_to_write(uint64_t rd_ptr, uint64_t wr_ptr, bool even);
        void    throttle_writer(const std::chrono::nanoseconds &elapsed, size_t count);
        void    writer_updates_fw_ptr(size_t written_blocks);
        bool    reset_file();

    private:
        //regulate the concurrent access to read and write pointers
        mutable std::shared_mutex m_driver_mutex;

        //emulate MSI-X on_data interrupts
        std::condition_variable m_irq_cond;
        mutable std::mutex m_irq_mutex;

        //suspend the card emulator thread when the DMA is full
        std::condition_variable m_writer_cond;
        mutable std::mutex m_stop_writer_mutex;

        unsigned int m_block_rate;
        bool m_repeat;
        bool m_rd_odd;
        bool m_wr_odd;
        FILE*  m_fp;
        std::thread m_writer_thread;
};

#endif /* TOHOST_BUFFER_H_ */