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 : }
|