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

}