Program Listing for File fromhost_buffer_file.cpp

Return to documentation for file (fromhost_buffer_file.cpp)

#include "fromhost_buffer.hpp"
#include "log.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()
{
    m_reader_thread.join();
    LOG_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;
    LOG_INFO("  cmem buffer [0x%x,0x%x] %lu Blocks", m_buffer->paddr, m_buffer->pend, m_buffer->size/1024);
    LOG_INFO("  cmem virtual address 0x%x", m_buffer->vaddr);
    LOG_INFO("  fw_ptr 0x%x", m_buffer->emu_fw_ptr);
    LOG_INFO("  pc_ptr 0x%x", m_buffer->pc_ptr);
    LOG_INFO("Spawning buffer-to-file reader thread...");
    m_reader_thread = std::thread(&FileFromHostBuffer::copy_from_dma_buffer_to_file, this);
}

void FileFromHostBuffer::dma_start_circular_trickle_buffer()
{
    std::unique_lock<std::shared_mutex> lock(m_driver_mutex);
    m_buffer->emu_fw_ptr = m_buffer->paddr;
    m_buffer->pend = m_buffer->paddr + m_trickle_config_size;
    m_fw_reading_trickle.store(true);
}


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 (m_buffer->pc_ptr - m_buffer->paddr);
}

// 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()
{
    LOG_INFO("read_from_dma_buffer thread started");
    uint64_t data_offset;
    int blocks_to_copy(0), 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, 500ms, [&] { 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();
        }

        LOG_TRACE("Blocks to copy %lu", blocks_to_copy);

        // What it does: if there is no trickle mode, it works normally. All the blocks are copied and the read pointer is advanced.
        // If the trickle mode is active (m_fw_reading_trickle is TRUE), it emulates the firmware by reading the buffer a maximum of 100 times to avoid an endless loop.
        int n_copies = 0;
        do
        {
            copied_blocks = 0;
            while (blocks_to_copy > copied_blocks)
            {
                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);
                size_t max_blocks = blocks_to_copy - copied_blocks;
                LOG_DBG("input ptr 0x%lx, buffer start 0x%lx, end 0x%lx size 0%lu", input_ptr, m_buffer->vaddr, m_buffer->vaddr + m_buffer->size, m_buffer->size);
                copied_blocks += m_file.block_write(input_ptr, max_blocks);
                LOG_DBG("%lu blocks copied to disk", copied_blocks);
            }
        }
        while (m_fw_reading_trickle.load() && n_copies++ < 100);
        dma_advance_read_ptr(copied_blocks * m_encoder.get_block_size());
    }
}

void FileFromHostBuffer::dma_advance_read_ptr(size_t bytes)
{
    LOG_TRACE("Advancing of %lu 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;
    }
}