LCOV - code coverage report
Current view: top level - src - tohost_buffer_file.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 138 175 78.9 %
Date: 2025-09-09 12:09:29 Functions: 16 22 72.7 %

          Line data    Source code
       1             : #include <chrono>
       2             : #include <fcntl.h>
       3             : #include <stdexcept>
       4             : #include "tohost_buffer.hpp"
       5             : #include "log.hpp"
       6             : 
       7          22 : FileToHostBuffer::FileToHostBuffer(int dmaid, std::shared_ptr<Device> d,
       8          22 :     std::string& filename, unsigned int block_rate, bool repeat)
       9          66 :     : ToHostBuffer(dmaid, d), m_block_rate(block_rate), m_repeat(repeat)
      10             : {
      11             :     // use open rather than fopen for fifos
      12          22 :     int fd = open(filename.c_str(), O_RDONLY | O_NONBLOCK);
      13          22 :     m_fp = fdopen(fd, "r");
      14          22 :     if (m_fp == nullptr) {
      15           0 :         throw std::runtime_error("Cannot open file");
      16             :     }
      17          22 :     LOG_INFO("Input from file %s in %s mode", filename.c_str(),  m_repeat ? "repeat" : "non-repeat");
      18          22 :     m_block_size = m_device->get_block_size();
      19          22 :     m_wr_odd = false;
      20          22 :     m_rd_odd = false;
      21          22 : }
      22             : 
      23             : 
      24          22 : FileToHostBuffer::~FileToHostBuffer()
      25             : {
      26          22 :     m_stop_flag = true;
      27          22 :     m_writer_thread.join();
      28          22 :     LOG_INFO("Writer thread joined");
      29          22 :     fclose(m_fp);
      30          22 : }
      31             : 
      32             : 
      33          22 : void FileToHostBuffer::allocate_buffer(size_t size,
      34             :                             const std::string& name,
      35             :                             bool vmem, bool free_previous_cmem)
      36             : {
      37          22 :     m_size = size;
      38          22 :     if (vmem) {
      39          22 :          m_buffer = std::make_unique<VmemBuffer>(size);
      40             :     }
      41             :     else {
      42           0 :         m_buffer = std::make_unique<CmemBuffer>(size, name, free_previous_cmem);
      43             :     }
      44          22 :     m_block_size = m_device->get_block_size();
      45          22 : }
      46             : 
      47             : 
      48          22 : void FileToHostBuffer::dma_start_continuous()
      49             : {
      50          22 :     m_buffer->pend = m_buffer->paddr + m_buffer->size;
      51          22 :     m_buffer->pc_ptr = m_buffer->paddr;
      52          22 :     m_buffer->emu_fw_ptr = m_buffer->paddr;
      53          22 :     LOG_INFO("  cmem buffer [0x%x,0x%x] %lu Blocks", m_buffer->paddr, m_buffer->pend, m_buffer->size/1024);
      54          22 :     LOG_INFO("  cmem virtual address 0x%x", m_buffer->vaddr);
      55          22 :     LOG_INFO("  fw_ptr 0x%x", m_buffer->emu_fw_ptr);
      56          22 :     LOG_INFO("  pc_ptr 0x%x", m_buffer->pc_ptr);
      57          22 :     LOG_INFO("Spawning file-to-buffer writer thread...");
      58          22 :     m_writer_thread = std::thread(&FileToHostBuffer::write_in_dma_buffer, this);
      59          22 : }
      60             : 
      61             : 
      62           0 : void FileToHostBuffer::dma_wait_for_data_irq()
      63             : {
      64           0 :     while(!m_stop_flag){
      65           0 :         std::unique_lock<std::mutex> lk(m_irq_mutex);
      66           0 :         m_irq_cond.wait(lk); //No condition given: this allows spurious wake-up calls...
      67           0 :     }
      68           0 : }
      69             : 
      70             : 
      71         177 : size_t FileToHostBuffer::dma_bytes_available()
      72             : {
      73         177 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
      74         177 :     bool even = (m_rd_odd == m_wr_odd);
      75         354 :     return dma_compute_bytes_to_read(m_buffer->pc_ptr, m_buffer->emu_fw_ptr, even);
      76         177 : }
      77             : 
      78             : 
      79   213448774 : bool FileToHostBuffer::dma_is_full()
      80             : {
      81   213448774 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
      82   426790782 :     return ((m_buffer->emu_fw_ptr == m_buffer->pc_ptr) and (m_rd_odd != m_wr_odd));
      83   213449163 : }
      84             : 
      85             : 
      86           0 : size_t FileToHostBuffer::dma_bytes_available_nowrap()
      87             : {
      88           0 :     size_t available = dma_bytes_available();
      89           0 :     return MIN(available, m_buffer->pend - m_buffer->pc_ptr);
      90             : }
      91             : 
      92             : 
      93           0 : uint64_t FileToHostBuffer::dma_get_write_ptr()
      94             : {
      95           0 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
      96           0 :     return (m_buffer->vaddr + (m_buffer->emu_fw_ptr - m_buffer->paddr));
      97           0 : }
      98             : 
      99             : 
     100           0 : uint64_t FileToHostBuffer::dma_get_read_ptr()
     101             : {
     102           0 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
     103           0 :     return (m_buffer->vaddr + (m_buffer->pc_ptr - m_buffer->paddr));
     104           0 : }
     105             : 
     106             : 
     107     6885755 : uint64_t FileToHostBuffer::dma_get_write_offset()
     108             : {
     109     6885755 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
     110     6885741 :     return (m_buffer->emu_fw_ptr - m_buffer->paddr);
     111     6885637 : }
     112             : 
     113             : 
     114          24 : uint64_t FileToHostBuffer::dma_get_read_offset()
     115             : {
     116          24 :     std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
     117          24 :     return (m_buffer->pc_ptr - m_buffer->paddr);
     118          24 : }
     119             : 
     120             : 
     121           0 : void FileToHostBuffer::dma_set_read_ptr_vaddr(uint64_t v_addr)
     122             : {
     123           0 :     uint64_t p_addr = m_buffer->paddr + (v_addr - m_buffer->vaddr);
     124           0 :     set_read_ptr_paddr(p_addr);
     125           0 : }
     126             : 
     127             : 
     128      545303 : void FileToHostBuffer::dma_set_read_ptr_paddr(uint64_t p_addr)
     129             : {
     130      545303 :     set_read_ptr_paddr(p_addr);
     131      545303 : }
     132             : 
     133             : 
     134      545303 : void FileToHostBuffer::set_read_ptr_paddr(uint64_t p_addr)
     135             : {
     136      545303 :     std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
     137      545303 :     if (p_addr == m_buffer->pc_ptr){ return; }
     138             : 
     139      541725 :     if ( p_addr < m_buffer->pc_ptr) {
     140        1285 :         unsigned int diff = (m_buffer->pc_ptr - p_addr);
     141        1285 :         if (diff < 16*1024*1024){
     142           0 :             LOG_ERR("rd ptr backwards of %u bytes. Old rd 0x%lx new 0x%lx ", diff, m_buffer->pc_ptr, p_addr);
     143             :         }
     144        1285 :         m_rd_odd = !m_rd_odd;
     145             :     }
     146             : 
     147      541725 :     m_buffer->pc_ptr = p_addr;
     148      541725 :     m_writer_cond.notify_one();
     149      545303 : }
     150             : 
     151             : 
     152           0 : void FileToHostBuffer::dma_advance_read_ptr(size_t bytes)
     153             : {
     154           0 :     if (bytes == 0){return;}
     155           0 :     std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
     156           0 :     m_buffer->pc_ptr += bytes;
     157           0 :     if(m_buffer->pc_ptr == m_buffer->pend){
     158           0 :         m_buffer->pc_ptr = m_buffer->paddr;
     159           0 :         m_rd_odd = !m_rd_odd;
     160             :     }
     161           0 :     m_writer_cond.notify_one();
     162           0 : }
     163             : 
     164             : 
     165             : // private
     166          22 : void FileToHostBuffer::write_in_dma_buffer()
     167             : {
     168          22 :     LOG_INFO("write_in_dma_buffer thread started");
     169          22 :     uint64_t rd_ptr, wr_ptr;
     170          22 :     bool even;
     171             : 
     172   213156569 :     while (!m_stop_flag){
     173             : 
     174             :         //Block on conditional variable if the DMA buffer is full
     175             :         //to prevent a continuous lock of m_driver_mutex.
     176             :         //This is realistic: an FLX card does not produce data nor interrupts
     177             :         //when the DMA is full.
     178   213156525 :         bool is_full = dma_is_full();
     179   213156525 :         if (is_full){
     180       43716 :             std::unique_lock<std::mutex> lk(m_stop_writer_mutex);
     181       43716 :             using namespace std::chrono_literals;
     182      130384 :             m_writer_cond.wait_for(lk, 100ms, [&]{return !dma_is_full();});
     183       43716 :         }
     184             : 
     185   213156525 :         {
     186   213156525 :             std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
     187   213156525 :             wr_ptr = m_buffer->emu_fw_ptr;
     188   213156525 :             rd_ptr = m_buffer->pc_ptr;
     189   213156525 :             even = (m_rd_odd == m_wr_odd);
     190   213156525 :         }
     191             : 
     192   213156525 :         size_t max_blocks = blocks_to_write(rd_ptr, wr_ptr, even);
     193   213156525 :         void *ptr = reinterpret_cast<void*>(m_buffer->vaddr + (wr_ptr - m_buffer->paddr));
     194             : 
     195   213156525 :         if ((wr_ptr + max_blocks*m_block_size) > m_buffer->pend) {
     196           0 :             throw std::range_error("Writing past end of buffer");
     197             :         }
     198             : 
     199   213156525 :         auto start = std::chrono::system_clock::now();
     200             : 
     201   213156525 :         size_t count = fread(ptr, m_block_size, max_blocks, m_fp);
     202   213156525 :         if (count > max_blocks) {
     203           0 :             throw std::range_error("fread wrote more blocks than allowed!");
     204             :         }
     205             : 
     206   213156525 :         auto end = std::chrono::system_clock::now();
     207             : 
     208   213156525 :         if (count > 0)
     209             :         {
     210     3048507 :             std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
     211     3048507 :             writer_updates_fw_ptr(count);
     212     3048507 :             LOG_TRACE("Written %lu blocks at 0x%lx, fw_ptr 0x%lx pc_ptr 0x%lx [0x%lx, 0x%lx]", count, m_buffer->vaddr + (wr_ptr - m_buffer->paddr), m_buffer->emu_fw_ptr, m_buffer->pc_ptr, m_buffer->paddr, m_buffer->pend);
     213     3048507 :             LOG_TRACE("Block header 0x%x, second word 0x%x", *(uint32_t*)ptr, *((uint32_t*)ptr + 1) );
     214     3048507 :         }
     215             : 
     216   213156525 :         m_irq_cond.notify_one();
     217   213156525 :         if (reset_file()) { break; }
     218             : 
     219   213156525 :         if(m_block_rate > 0 && count > 0){
     220     1025604 :             auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
     221     1025604 :             throttle_writer(elapsed, count);
     222             :         }
     223             :     }
     224          22 : }
     225             : 
     226             : 
     227   213156525 : bool FileToHostBuffer::reset_file()
     228             : {
     229   213156525 :     bool stop = false;
     230   213156525 :     if (feof(m_fp)){
     231     1724007 :         if (m_repeat){
     232     1724007 :             fseek(m_fp, 0, SEEK_SET);
     233             :         } else {
     234             :             stop = true;
     235             :         }
     236             :     }
     237   213156525 :     return stop;
     238             : }
     239             : 
     240             : 
     241   213156525 : size_t FileToHostBuffer::blocks_to_write(uint64_t rd_ptr, uint64_t wr_ptr, bool even)
     242             : {
     243   213156525 :     size_t max_blocks{0};
     244   213156525 :     if (wr_ptr > rd_ptr) {
     245             :         //Only up to end of buffer
     246     1566919 :         max_blocks = (m_size - (wr_ptr-m_buffer->paddr)) / m_block_size;
     247             :     }
     248   211589606 :     else if (wr_ptr < rd_ptr) {
     249   211582088 :         max_blocks = (rd_ptr - wr_ptr) / m_block_size;
     250             :     }
     251             :     else {
     252        7518 :         if (even) {
     253             :             //In this case the DMA buffer is empty, but we write blocks
     254             :             //only until the end of the buffer.
     255        7145 :             max_blocks = (m_size - (wr_ptr-m_buffer->paddr)) / m_block_size;
     256             :         } else {
     257             :             max_blocks = 0;
     258             :         }
     259             :     }
     260   213156525 :     max_blocks = limit_block_rate(max_blocks);
     261   213156525 :     return max_blocks;
     262             : }
     263             : 
     264             : 
     265   213156525 : size_t FileToHostBuffer::limit_block_rate(size_t max_blocks)
     266             : {
     267   213156525 :     if (m_block_rate == 0) {
     268             :         return max_blocks;
     269   107357290 :     } else if (m_block_rate < 6000){
     270        1983 :         return (max_blocks > 1) ? 1 : max_blocks;
     271   107355307 :     } else if (m_block_rate < 12000){
     272           0 :         return (max_blocks > 2) ? 2 : max_blocks;
     273   107355307 :     } else if (m_block_rate < 120000){
     274       91160 :         return (max_blocks > 10) ? 10 : max_blocks;
     275             :     } else {
     276   107264147 :         return (max_blocks > 32) ? 32 : max_blocks;
     277             :     }
     278             : }
     279             : 
     280             : 
     281     1025604 : void FileToHostBuffer::throttle_writer(const std::chrono::nanoseconds & elapsed, size_t count)
     282             : {
     283     1025604 :     long block_read_time = elapsed.count()/count;
     284     1025604 :     long target_time = 1.0e9*(1.0/(double)m_block_rate);
     285     1025604 :     long sleep_time = target_time - block_read_time;
     286     1025604 :     std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_time));
     287     1025604 : }
     288             : 
     289             : 
     290     3048507 : void FileToHostBuffer::writer_updates_fw_ptr(size_t written_blocks)
     291             : {
     292     3048507 :     LOG_TRACE("New wr ptr 0x%lx start 0x%lx end 0x%lx", m_buffer->emu_fw_ptr, m_buffer->paddr, m_buffer->pend);
     293     3048507 :     m_buffer->emu_fw_ptr += (m_block_size*written_blocks);
     294     3048507 :     if (m_buffer->emu_fw_ptr > m_buffer->pend){
     295           0 :         throw std::range_error("Moving write pointer beyond end of buffer");
     296             :     }
     297     3048507 :     if (m_buffer->emu_fw_ptr == m_buffer->pend){
     298        1171 :         m_buffer->emu_fw_ptr = m_buffer->paddr;
     299        1171 :         m_wr_odd = !m_wr_odd;
     300             :     }
     301     3048507 : }

Generated by: LCOV version 1.0