Program Listing for File fromhost_buffer.hpp

Return to documentation for file (fromhost_buffer.hpp)

#ifndef FROMHOST_BUFFER_H_
#define FROMHOST_BUFFER_H_

#include <cstddef>
#include <cstdint>
#include <stdio.h>
#include <thread>
#include <mutex>
#include <atomic>
#include <shared_mutex>
#include <condition_variable>
#include "log.hpp"

#include "device.hpp"
#include "encoder.hpp"
#include "cmem_buffer.hpp"
#include "disk_io.hpp"
#include "fromhost_monitor.hpp"

class FromHostBuffer
{
    public:

        FromHostBuffer(std::shared_ptr<Device> d) :
            m_device(d), m_run_flag(true), m_size(0), m_number_of_writers(0),
            m_encoder(), m_mon(), m_mon_prev(), m_trickle_config_size(0)
        {};

        std::shared_ptr<Device> get_device(){return m_device;}

        [[nodiscard]] size_t get_size() const{return m_size;}


        size_t get_trickle_config_size(){return m_trickle_config_size;}

        void set_trickle_config_size(size_t size){m_trickle_config_size = size;}

        void set_encoder_data_format(int format){m_encoder.set_data_format(format);}

        [[nodiscard]] int get_dmaid() const{return m_dmaid;}

        void set_dmaid(int dma_id)
        {
            if(m_dmaid == -1) m_dmaid = dma_id;
            //else throw std::runtime_error("DMA buffer already initialized with id: " + std::to_string(m_dmaid));
        }


        void stop(){m_run_flag = false;}

        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);};

        void increment_writer_counter (){++m_number_of_writers;}

        [[nodiscard]] bool has_multiple_writers() const{return m_number_of_writers > 1;}

        size_t compute_msg_dma_occupancy(size_t size){return m_encoder.compute_max_msg_occupancy(size);};

        void encode_and_write(uint64_t elink, const uint8_t *source, size_t size, bool trickle = false);

        uint32_t dma_get_free_MB() {return dma_free_bytes()/1024/1024;}

        FromHostDmaStats get_monitoring_data();

        virtual size_t   dma_free_bytes()                            = 0;

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

        std::mutex m_buffer_mutex; //allow FromHostWriters to lock this

    protected:
        virtual void set_oneshot_trickle_buffer(size_t config_size) = 0;
        virtual void     dma_start_continuous()                     = 0;
        virtual void     dma_start_circular_trickle_buffer()        = 0;
        virtual bool     dma_is_full()                              = 0;
        virtual uint64_t dma_get_read_offset()                      = 0;
        virtual uint64_t dma_get_write_ptr()                        = 0;
        virtual uint64_t dma_get_write_offset()                     = 0;
        virtual void     dma_set_write_offset(uint64_t offset)      = 0;
        virtual void     dma_advance_write_ptr(size_t bytes)        = 0;

    protected:
        int m_dmaid = -1; // -1 is just to check if the buffer is not initialized
        std::shared_ptr<Device> m_device;
        std::atomic<bool> m_run_flag{true};
        size_t m_size;
        int m_number_of_writers;
        Encoder m_encoder;
        FromHostDmaStats m_mon;
        FromHostDmaStats m_mon_prev; //to compute rates
        size_t m_trickle_config_size;
        std::unique_ptr<DmaBuffer> m_buffer;

        size_t dma_compute_free_bytes(uint64_t fw_rd_ptr, uint64_t pc_wr_ptr, bool even);
};


inline size_t FromHostBuffer::dma_compute_free_bytes(uint64_t fw_rd_ptr, uint64_t pc_wr_ptr, bool even)
{
    size_t value{0};
    if(pc_wr_ptr < fw_rd_ptr ) {
        value = fw_rd_ptr - pc_wr_ptr;
    }
    else if( pc_wr_ptr > fw_rd_ptr ) {
        value = m_buffer->size - (pc_wr_ptr - fw_rd_ptr);
    }
    else { // pc_wr_ptr == fw_rd_ptr
        if( even ) {
            value = m_buffer->size; // Buffer empty
        } //else value = 0
    }
    return value;
}


class FlxFromHostBuffer : public FromHostBuffer {

    public:
        explicit FlxFromHostBuffer(std::shared_ptr<Device> d);
        ~FlxFromHostBuffer();

        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_start_circular_trickle_buffer()        override;
        void set_oneshot_trickle_buffer(size_t config_size) override;
        size_t   dma_free_bytes()                           override;
        bool     dma_is_full()                              override;
        uint64_t dma_get_read_offset()                      override;
        uint64_t dma_get_write_offset()                     override;
        uint64_t dma_get_write_ptr()                        override;
        void     dma_set_write_offset(uint64_t addr)        override;
        void     dma_advance_write_ptr(size_t bytes)        override;
};


class FileFromHostBuffer : public FromHostBuffer {

    public:
        explicit FileFromHostBuffer(std::shared_ptr<Device> d,
            std::string& filename, bool fifo);
        ~FileFromHostBuffer();

        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_start_circular_trickle_buffer()        override;
        void set_oneshot_trickle_buffer(size_t config_size) override {m_fw_reading_trickle.store(false);};
        bool     dma_is_full()                              override;
        uint64_t dma_get_read_offset()                      override;
        size_t   dma_free_bytes()                           override;
        uint64_t dma_get_write_offset()                     override;
        uint64_t dma_get_write_ptr()                        override;
        void     dma_set_write_offset(uint64_t addr)        override;
        void     dma_advance_write_ptr(size_t bytes)        override;

    private:
        bool     dma_is_empty();
        size_t   dma_bytes_to_consume();
        void     dma_advance_read_ptr(size_t bytes);
        void     dma_set_read_offset(uint64_t addr);
        void     set_write_ptr_paddr(uint64_t p_addr);
        void     copy_from_dma_buffer_to_file();

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

        //suspend the card emulator thread when the DMA is empty
        std::condition_variable m_reader_cond;
        mutable std::mutex m_wake_reader_mutex;

        DiskIO m_file;
        bool m_rd_odd;
        bool m_wr_odd;
        std::atomic<bool> m_fw_reading_trickle {false};
        std::thread m_reader_thread;
};

#endif /* FROMHOST_BUFFER_H_ */