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_ */