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 175 : size_t FileToHostBuffer::dma_bytes_available()
72 : {
73 175 : std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
74 175 : bool even = (m_rd_odd == m_wr_odd);
75 350 : return dma_compute_bytes_to_read(m_buffer->pc_ptr, m_buffer->emu_fw_ptr, even);
76 175 : }
77 :
78 :
79 239630236 : bool FileToHostBuffer::dma_is_full()
80 : {
81 239630236 : std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
82 479074630 : return ((m_buffer->emu_fw_ptr == m_buffer->pc_ptr) and (m_rd_odd != m_wr_odd));
83 239630610 : }
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 7564395 : uint64_t FileToHostBuffer::dma_get_write_offset()
108 : {
109 7564395 : std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
110 7564450 : return (m_buffer->emu_fw_ptr - m_buffer->paddr);
111 7564492 : }
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 553586 : void FileToHostBuffer::dma_set_read_ptr_paddr(uint64_t p_addr)
129 : {
130 553586 : set_read_ptr_paddr(p_addr);
131 553586 : }
132 :
133 :
134 553586 : void FileToHostBuffer::set_read_ptr_paddr(uint64_t p_addr)
135 : {
136 553586 : std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
137 553586 : if (p_addr == m_buffer->pc_ptr){ return; }
138 :
139 550528 : if ( p_addr < m_buffer->pc_ptr) {
140 1327 : unsigned int diff = (m_buffer->pc_ptr - p_addr);
141 1327 : 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 1327 : m_rd_odd = !m_rd_odd;
145 : }
146 :
147 550528 : m_buffer->pc_ptr = p_addr;
148 550528 : m_writer_cond.notify_one();
149 553586 : }
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 239258616 : 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 239258572 : bool is_full = dma_is_full();
179 239258572 : if (is_full){
180 84828 : std::unique_lock<std::mutex> lk(m_stop_writer_mutex);
181 84828 : using namespace std::chrono_literals;
182 250645 : m_writer_cond.wait_for(lk, 100ms, [&]{return !dma_is_full();});
183 84828 : }
184 :
185 239258572 : {
186 239258572 : std::shared_lock<std::shared_mutex> lock(m_driver_mutex);
187 239258572 : wr_ptr = m_buffer->emu_fw_ptr;
188 239258572 : rd_ptr = m_buffer->pc_ptr;
189 239258572 : even = (m_rd_odd == m_wr_odd);
190 239258572 : }
191 :
192 239258572 : size_t max_blocks = blocks_to_write(rd_ptr, wr_ptr, even);
193 239258572 : void *ptr = reinterpret_cast<void*>(m_buffer->vaddr + (wr_ptr - m_buffer->paddr));
194 :
195 239258572 : 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 239258572 : auto start = std::chrono::system_clock::now();
200 :
201 239258572 : size_t count = fread(ptr, m_block_size, max_blocks, m_fp);
202 239258572 : if (count > max_blocks) {
203 0 : throw std::range_error("fread wrote more blocks than allowed!");
204 : }
205 :
206 239258572 : auto end = std::chrono::system_clock::now();
207 :
208 239258572 : if (count > 0)
209 : {
210 3207557 : std::scoped_lock<std::shared_mutex> lock(m_driver_mutex);
211 3207557 : writer_updates_fw_ptr(count);
212 3207557 : 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 3207557 : LOG_TRACE("Block header 0x%x, second word 0x%x", *(uint32_t*)ptr, *((uint32_t*)ptr + 1) );
214 3207557 : }
215 :
216 239258572 : m_irq_cond.notify_one();
217 239258572 : if (reset_file()) { break; }
218 :
219 239258572 : if(m_block_rate > 0 && count > 0){
220 1044583 : auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
221 1044583 : throttle_writer(elapsed, count);
222 : }
223 : }
224 22 : }
225 :
226 :
227 239258572 : bool FileToHostBuffer::reset_file()
228 : {
229 239258572 : bool stop = false;
230 239258572 : if (feof(m_fp)){
231 1836611 : if (m_repeat){
232 1836611 : fseek(m_fp, 0, SEEK_SET);
233 : } else {
234 : stop = true;
235 : }
236 : }
237 239258572 : return stop;
238 : }
239 :
240 :
241 239258572 : size_t FileToHostBuffer::blocks_to_write(uint64_t rd_ptr, uint64_t wr_ptr, bool even)
242 : {
243 239258572 : size_t max_blocks{0};
244 239258572 : if (wr_ptr > rd_ptr) {
245 : //Only up to end of buffer
246 1551161 : max_blocks = (m_size - (wr_ptr-m_buffer->paddr)) / m_block_size;
247 : }
248 237707411 : else if (wr_ptr < rd_ptr) {
249 237699388 : max_blocks = (rd_ptr - wr_ptr) / m_block_size;
250 : }
251 : else {
252 8023 : if (even) {
253 : //In this case the DMA buffer is empty, but we write blocks
254 : //only until the end of the buffer.
255 7654 : max_blocks = (m_size - (wr_ptr-m_buffer->paddr)) / m_block_size;
256 : } else {
257 : max_blocks = 0;
258 : }
259 : }
260 239258572 : max_blocks = limit_block_rate(max_blocks);
261 239258572 : return max_blocks;
262 : }
263 :
264 :
265 239258572 : size_t FileToHostBuffer::limit_block_rate(size_t max_blocks)
266 : {
267 239258572 : if (m_block_rate == 0) {
268 : return max_blocks;
269 129495456 : } else if (m_block_rate < 6000){
270 2026 : return (max_blocks > 1) ? 1 : max_blocks;
271 129493430 : } else if (m_block_rate < 12000){
272 0 : return (max_blocks > 2) ? 2 : max_blocks;
273 129493430 : } else if (m_block_rate < 120000){
274 92594 : return (max_blocks > 10) ? 10 : max_blocks;
275 : } else {
276 129400836 : return (max_blocks > 32) ? 32 : max_blocks;
277 : }
278 : }
279 :
280 :
281 1044583 : void FileToHostBuffer::throttle_writer(const std::chrono::nanoseconds & elapsed, size_t count)
282 : {
283 1044583 : long block_read_time = elapsed.count()/count;
284 1044583 : long target_time = 1.0e9*(1.0/(double)m_block_rate);
285 1044583 : long sleep_time = target_time - block_read_time;
286 1044583 : std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_time));
287 1044583 : }
288 :
289 :
290 3207557 : void FileToHostBuffer::writer_updates_fw_ptr(size_t written_blocks)
291 : {
292 3207557 : 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 3207557 : m_buffer->emu_fw_ptr += (m_block_size*written_blocks);
294 3207557 : if (m_buffer->emu_fw_ptr > m_buffer->pend){
295 0 : throw std::range_error("Moving write pointer beyond end of buffer");
296 : }
297 3207557 : if (m_buffer->emu_fw_ptr == m_buffer->pend){
298 1253 : m_buffer->emu_fw_ptr = m_buffer->paddr;
299 1253 : m_wr_odd = !m_wr_odd;
300 : }
301 3207557 : }
|