Program Listing for File fromhost_buffer_file.cpp
↰ Return to documentation for file (fromhost_buffer_file.cpp)
#include "fromhost_buffer.hpp"
#include <cstdint>
FileFromHostBuffer::FileFromHostBuffer(std::shared_ptr<Device> d, std::string &filename, bool fifo)
: FromHostBuffer(d),
m_file(filename, m_encoder.get_block_size(), DiskIO::FWRITE, fifo),
m_rd_odd(false),
m_wr_odd(false){};
FileFromHostBuffer::~FileFromHostBuffer()
{
if(m_reader_thread.joinable()) {
m_reader_thread.join();
}
ERS_INFO("Reader thread joined");
}
void FileFromHostBuffer::allocate_buffer(size_t size,
const std::string &name,
bool vmem, bool free_previous_cmem)
{
m_size = size;
if (vmem)
{
m_buffer = std::make_unique<VmemBuffer>(size);
}
else
{
m_buffer = std::make_unique<CmemBuffer>(size, name, free_previous_cmem);
}
uint8_t *vaddr = reinterpret_cast<uint8_t *>(m_buffer->vaddr);
m_encoder.set_destination_parameters(vaddr, m_buffer->size);
}
void FileFromHostBuffer::dma_start_continuous()
{
m_buffer->pend = m_buffer->paddr + m_buffer->size;
m_buffer->pc_ptr = m_buffer->paddr;
m_buffer->emu_fw_ptr = m_buffer->paddr;
ERS_INFO(std::format(" cmem buffer [{:#x},{:#x}] {} Blocks", m_buffer->paddr, m_buffer->pend, m_buffer->size/1024));
ERS_INFO(std::format(" cmem virtual address {:#x}", m_buffer->vaddr));
ERS_INFO(std::format(" fw_ptr {:#x}", m_buffer->emu_fw_ptr));
ERS_INFO(std::format(" pc_ptr {:#x}", m_buffer->pc_ptr));
ERS_INFO("Spawning buffer-to-file reader thread...");
m_reader_thread = std::jthread(&FileFromHostBuffer::copy_from_dma_buffer_to_file, this);
}
void FileFromHostBuffer::dma_start_circular_trickle_buffer()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
m_buffer->emu_fw_ptr = m_buffer->paddr;
m_buffer->pend = m_buffer->paddr + do_dma_get_write_offset();
m_buffer->pc_ptr = m_buffer->pend;
m_fw_reading_trickle.store(true);
m_reader_thread = std::jthread(&FileFromHostBuffer::copy_trickle_buffer_to_file, this);
}
size_t FileFromHostBuffer::dma_free_bytes()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
bool even = (m_rd_odd == m_wr_odd);
return dma_compute_free_bytes(m_buffer->emu_fw_ptr, m_buffer->pc_ptr, even);
}
bool FileFromHostBuffer::dma_is_full()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
return ((m_buffer->emu_fw_ptr == m_buffer->pc_ptr) and (m_rd_odd != m_wr_odd));
}
bool FileFromHostBuffer::dma_is_empty()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
return ((m_buffer->emu_fw_ptr == m_buffer->pc_ptr) and (m_rd_odd == m_wr_odd));
}
// offset read (fw emu)
uint64_t FileFromHostBuffer::dma_get_read_offset()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
return (m_buffer->emu_fw_ptr - m_buffer->paddr);
}
// offset write (pc)
uint64_t FileFromHostBuffer::dma_get_write_offset()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
return do_dma_get_write_offset();
}
// offset write (pc)
void FileFromHostBuffer::dma_set_write_offset(uint64_t offset)
{
uint64_t p_addr = m_buffer->paddr + offset;
set_write_ptr_paddr(p_addr);
}
// vaddr
uint64_t FileFromHostBuffer::dma_get_write_ptr()
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
return (m_buffer->pc_ptr - m_buffer->paddr + m_buffer->vaddr);
}
void FileFromHostBuffer::dma_advance_write_ptr(size_t bytes)
{
if (bytes == 0)
{
return;
}
std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
m_buffer->emu_fw_ptr += bytes;
if (m_buffer->emu_fw_ptr == m_buffer->pend)
{
m_buffer->emu_fw_ptr = m_buffer->paddr;
m_wr_odd = !m_wr_odd;
}
}
void FileFromHostBuffer::set_write_ptr_paddr(uint64_t p_addr)
{
// write ptr := pc_ptr
std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
if (p_addr == m_buffer->pc_ptr)
{
return;
}
if (p_addr < m_buffer->pc_ptr)
{
unsigned int diff = (p_addr - m_buffer->pc_ptr);
if (diff > 0.1 * m_buffer->size)
{
m_wr_odd = !m_wr_odd;
}
}
m_buffer->pc_ptr = p_addr;
m_reader_cond.notify_one();
}
void FileFromHostBuffer::copy_from_dma_buffer_to_file()
{
ERS_INFO("read_from_dma_buffer thread started");
static constexpr std::chrono::milliseconds READER_WAIT_TIMEOUT{500};
uint64_t data_offset{0};
int blocks_to_copy{0};
int copied_blocks{0};
while (m_run_flag.load())
{
{
// wait for data in the FromHost DMA buffer
std::unique_lock<std::mutex> lk(m_wake_reader_mutex);
using namespace std::chrono_literals;
m_reader_cond.wait_for(lk, READER_WAIT_TIMEOUT, [&] { return !dma_is_empty(); });
}
{
// copy current addresses
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
bool even = (m_rd_odd == m_wr_odd);
data_offset = m_buffer->emu_fw_ptr - m_buffer->paddr;
blocks_to_copy = (m_buffer->size - dma_compute_free_bytes(m_buffer->emu_fw_ptr, m_buffer->pc_ptr, even)) / m_encoder.get_block_size();
}
ERS_DEBUG(1, std::format("Blocks to copy {}", blocks_to_copy));
copied_blocks = 0;
while (blocks_to_copy > copied_blocks)
{
const uint64_t input = (data_offset + copied_blocks * m_encoder.get_block_size()) % m_buffer->size;
void *input_ptr = reinterpret_cast<void *>(input + m_buffer->vaddr);
const size_t max_blocks = blocks_to_copy - copied_blocks;
ERS_DEBUG(1, std::format("input ptr {}, buffer start {:#x}, end {:#x} size {}", input_ptr, m_buffer->vaddr, m_buffer->vaddr + m_buffer->size, m_buffer->size));
copied_blocks += m_file.block_write(input_ptr, max_blocks);
ERS_DEBUG(1, std::format("{} blocks copied to disk", copied_blocks));
}
dma_advance_read_ptr(copied_blocks * m_encoder.get_block_size());
}
}
void FileFromHostBuffer::dma_advance_read_ptr(size_t bytes)
{
ERS_DEBUG(1, std::format("Advancing of {} bytes", bytes));
if (bytes == 0)
{
return;
}
std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
m_buffer->emu_fw_ptr += bytes;
if (m_buffer->emu_fw_ptr == m_buffer->pend)
{
m_buffer->emu_fw_ptr = m_buffer->paddr;
m_rd_odd = !m_rd_odd;
}
}
uint64_t FileFromHostBuffer::do_dma_get_write_offset()
{
return (m_buffer->pc_ptr - m_buffer->paddr);
}
void FileFromHostBuffer::copy_trickle_buffer_to_file()
{
ERS_INFO("Trickle buffer to file copy started");
uint64_t data_offset{0};
int blocks_to_copy{0};
int copied_blocks{0};
uint64_t start_ptr{};
uint64_t end_ptr{};
{
std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
start_ptr = m_buffer->paddr; // Always start from beginning in trickle mode
end_ptr = m_buffer->pc_ptr; // The buffer ends at the end of trickle config, not at the actual buffer size
blocks_to_copy = (end_ptr - start_ptr) / m_encoder.get_block_size();
}
ERS_INFO(std::format("Trickle buffer: copying {} blocks", blocks_to_copy));
while (copied_blocks < blocks_to_copy)
{
const size_t blocks_this_chunk = std::min(blocks_to_copy - copied_blocks, 1024);
const uint64_t input = (data_offset + copied_blocks * m_encoder.get_block_size());
const void *input_ptr = reinterpret_cast<void *>(input + m_buffer->vaddr);
ERS_DEBUG(1, std::format("Copying chunk at offset {:#x}, {} blocks", input, blocks_this_chunk));
const size_t blocks_written = m_file.block_write(input_ptr, blocks_this_chunk);
copied_blocks += blocks_written;
m_buffer->emu_fw_ptr = start_ptr + (copied_blocks * m_encoder.get_block_size());
ERS_DEBUG(1, std::format("Progress: {} of {} blocks copied to disk", copied_blocks, blocks_to_copy));
}
std::shared_lock<std::shared_mutex> unlock(m_driver_mutex);
set_oneshot_trickle_buffer();
ERS_DEBUG(1, std::format("Trickle buffer fully copied: {} blocks ({} bytes) written to file",
copied_blocks, copied_blocks * m_encoder.get_block_size()));
}