1 // 2 // Copyright 2020 Ettus Research, a National Instruments Brand 3 // 4 // SPDX-License-Identifier: GPL-3.0-or-later 5 // 6 7 #include <uhd/convert.hpp> 8 #include <uhd/exception.hpp> 9 #include <uhd/rfnoc/defaults.hpp> 10 #include <uhd/rfnoc/multichan_register_iface.hpp> 11 #include <uhd/rfnoc/property.hpp> 12 #include <uhd/rfnoc/registry.hpp> 13 #include <uhd/rfnoc/replay_block_control.hpp> 14 #include <uhd/types/stream_cmd.hpp> 15 #include <uhd/utils/math.hpp> 16 #include <uhdlib/utils/compat_check.hpp> 17 #include <uhdlib/utils/narrow.hpp> 18 #include <string> 19 20 using namespace uhd::rfnoc; 21 22 // Block compatability version 23 const uint16_t replay_block_control::MINOR_COMPAT = 0; 24 const uint16_t replay_block_control::MAJOR_COMPAT = 1; 25 26 // NoC block address space 27 const uint32_t replay_block_control::REPLAY_ADDR_W = 8; 28 const uint32_t replay_block_control::REPLAY_BLOCK_OFFSET = 29 1 << replay_block_control::REPLAY_ADDR_W; // 256 bytes 30 31 // Register offsets 32 const uint32_t replay_block_control::REG_COMPAT_ADDR = 0x00; 33 const uint32_t replay_block_control::REG_MEM_SIZE_ADDR = 0x04; 34 const uint32_t replay_block_control::REG_REC_RESTART_ADDR = 0x08; 35 const uint32_t replay_block_control::REG_REC_BASE_ADDR_LO_ADDR = 0x10; 36 const uint32_t replay_block_control::REG_REC_BASE_ADDR_HI_ADDR = 0x14; 37 const uint32_t replay_block_control::REG_REC_BUFFER_SIZE_LO_ADDR = 0x18; 38 const uint32_t replay_block_control::REG_REC_BUFFER_SIZE_HI_ADDR = 0x1C; 39 const uint32_t replay_block_control::REG_REC_FULLNESS_LO_ADDR = 0x20; 40 const uint32_t replay_block_control::REG_REC_FULLNESS_HI_ADDR = 0x24; 41 const uint32_t replay_block_control::REG_PLAY_BASE_ADDR_LO_ADDR = 0x28; 42 const uint32_t replay_block_control::REG_PLAY_BASE_ADDR_HI_ADDR = 0x2C; 43 const uint32_t replay_block_control::REG_PLAY_BUFFER_SIZE_LO_ADDR = 0x30; 44 const uint32_t replay_block_control::REG_PLAY_BUFFER_SIZE_HI_ADDR = 0x34; 45 const uint32_t replay_block_control::REG_PLAY_CMD_NUM_WORDS_LO_ADDR = 0x38; 46 const uint32_t replay_block_control::REG_PLAY_CMD_NUM_WORDS_HI_ADDR = 0x3C; 47 const uint32_t replay_block_control::REG_PLAY_CMD_TIME_LO_ADDR = 0x40; 48 const uint32_t replay_block_control::REG_PLAY_CMD_TIME_HI_ADDR = 0x44; 49 const uint32_t replay_block_control::REG_PLAY_CMD_ADDR = 0x48; 50 const uint32_t replay_block_control::REG_PLAY_WORDS_PER_PKT_ADDR = 0x4C; 51 const uint32_t replay_block_control::REG_PLAY_ITEM_SIZE_ADDR = 0x50; 52 53 // Stream commands 54 const uint32_t replay_block_control::PLAY_CMD_STOP = 0; 55 const uint32_t replay_block_control::PLAY_CMD_FINITE = 1; 56 const uint32_t replay_block_control::PLAY_CMD_CONTINUOUS = 2; 57 58 // Mask bits 59 constexpr uint32_t PLAY_COMMAND_TIMED_BIT = 31; 60 constexpr uint32_t PLAY_COMMAND_TIMED_MASK = uint32_t(1) << PLAY_COMMAND_TIMED_BIT; 61 constexpr uint32_t PLAY_COMMAND_MASK = 3; 62 63 // User property names 64 const char* const PROP_KEY_RECORD_OFFSET = "record_offset"; 65 const char* const PROP_KEY_RECORD_SIZE = "record_size"; 66 const char* const PROP_KEY_PLAY_OFFSET = "play_offset"; 67 const char* const PROP_KEY_PLAY_SIZE = "play_size"; 68 const char* const PROP_KEY_PKT_SIZE = "packet_size"; 69 70 class replay_block_control_impl : public replay_block_control 71 { 72 public: RFNOC_BLOCK_CONSTRUCTOR(replay_block_control)73 RFNOC_BLOCK_CONSTRUCTOR(replay_block_control), 74 _replay_reg_iface(*this, 0, REPLAY_BLOCK_OFFSET), 75 _num_input_ports(get_num_input_ports()), 76 _num_output_ports(get_num_output_ports()), 77 _fpga_compat(_replay_reg_iface.peek32(REG_COMPAT_ADDR)), 78 _word_size( 79 uint16_t((_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) >> 16) & 0xFFFF) / 8), 80 _mem_size(uint64_t(1ULL << (_replay_reg_iface.peek32(REG_MEM_SIZE_ADDR) & 0xFFFF))) 81 { 82 UHD_ASSERT_THROW(get_num_input_ports() == get_num_output_ports()); 83 uhd::assert_fpga_compat(MAJOR_COMPAT, 84 MINOR_COMPAT, 85 _fpga_compat, 86 get_unique_id(), 87 get_unique_id(), 88 false /* Let it slide if minors mismatch */ 89 ); 90 91 // Initialize record properties 92 _record_type.reserve(_num_input_ports); 93 _record_offset.reserve(_num_input_ports); 94 _record_size.reserve(_num_input_ports); 95 for (size_t port = 0; port < _num_input_ports; port++) { 96 _register_input_props(port); 97 _replay_reg_iface.poke64( 98 REG_REC_BASE_ADDR_LO_ADDR, _record_offset.at(port).get(), port); 99 _replay_reg_iface.poke64( 100 REG_REC_BUFFER_SIZE_LO_ADDR, _record_size.at(port).get(), port); 101 } 102 103 // Initialize playback properties 104 _play_type.reserve(_num_output_ports); 105 _play_offset.reserve(_num_output_ports); 106 _play_size.reserve(_num_output_ports); 107 _packet_size.reserve(_num_output_ports); 108 for (size_t port = 0; port < _num_output_ports; port++) { 109 _register_output_props(port); 110 _replay_reg_iface.poke32(REG_PLAY_ITEM_SIZE_ADDR, 111 uhd::convert::get_bytes_per_item(_play_type.at(port).get()), 112 port); 113 _replay_reg_iface.poke64( 114 REG_PLAY_BASE_ADDR_LO_ADDR, _play_offset.at(port).get(), port); 115 _replay_reg_iface.poke64( 116 REG_PLAY_BUFFER_SIZE_LO_ADDR, _play_size.at(port).get(), port); 117 _replay_reg_iface.poke32(REG_PLAY_WORDS_PER_PKT_ADDR, 118 (_packet_size.at(port).get() - CHDR_MAX_LEN_HDR) / _word_size, 119 port); 120 } 121 } 122 123 /************************************************************************** 124 * Replay Control API 125 **************************************************************************/ record(const uint64_t offset,const uint64_t size,const size_t port)126 void record(const uint64_t offset, const uint64_t size, const size_t port) 127 { 128 set_property<uint64_t>( 129 PROP_KEY_RECORD_OFFSET, offset, {res_source_info::USER, port}); 130 set_property<uint64_t>(PROP_KEY_RECORD_SIZE, size, {res_source_info::USER, port}); 131 132 // The pointers to the new record buffer space must be set 133 record_restart(port); 134 } 135 record_restart(const size_t port)136 void record_restart(const size_t port) 137 { 138 // Ensure that the buffer is properly configured before recording 139 _validate_record_buffer(port); 140 // Any value written to this register causes a record restart 141 _replay_reg_iface.poke32(REG_REC_RESTART_ADDR, 0, port); 142 } 143 play(const uint64_t offset,const uint64_t size,const size_t port,const uhd::time_spec_t time_spec,const bool repeat)144 void play(const uint64_t offset, 145 const uint64_t size, 146 const size_t port, 147 const uhd::time_spec_t time_spec, 148 const bool repeat) 149 { 150 config_play(offset, size, port); 151 uhd::stream_cmd_t play_cmd = 152 repeat ? uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS) 153 : uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); 154 play_cmd.num_samps = size / get_play_item_size(port); 155 play_cmd.time_spec = time_spec; 156 play_cmd.stream_now = (time_spec == 0.0); 157 issue_stream_cmd(play_cmd, port); 158 } 159 stop(const size_t port)160 void stop(const size_t port) 161 { 162 uhd::stream_cmd_t stop_cmd = 163 uhd::stream_cmd_t(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); 164 issue_stream_cmd(stop_cmd, port); 165 } 166 get_mem_size() const167 uint64_t get_mem_size() const 168 { 169 return _mem_size; 170 } 171 get_word_size() const172 uint64_t get_word_size() const 173 { 174 return _word_size; 175 } 176 177 /************************************************************************** 178 * Record Buffer State API 179 **************************************************************************/ get_record_offset(const size_t port) const180 uint64_t get_record_offset(const size_t port) const 181 { 182 return _record_offset.at(port).get(); 183 } 184 get_record_size(const size_t port) const185 uint64_t get_record_size(const size_t port) const 186 { 187 return _record_size.at(port).get(); 188 } 189 get_record_fullness(const size_t port)190 uint64_t get_record_fullness(const size_t port) 191 { 192 return _replay_reg_iface.peek64(REG_REC_FULLNESS_LO_ADDR, port); 193 } 194 get_record_type(const size_t port) const195 io_type_t get_record_type(const size_t port) const 196 { 197 return _record_type.at(port).get(); 198 } 199 get_record_item_size(const size_t port) const200 virtual size_t get_record_item_size(const size_t port) const 201 { 202 return uhd::convert::get_bytes_per_item(get_record_type(port)); 203 } 204 205 /************************************************************************** 206 * Playback State API 207 **************************************************************************/ get_play_offset(const size_t port) const208 uint64_t get_play_offset(const size_t port) const 209 { 210 return _play_offset.at(port).get(); 211 } 212 get_play_size(const size_t port) const213 uint64_t get_play_size(const size_t port) const 214 { 215 return _play_size.at(port).get(); 216 } 217 get_max_items_per_packet(const size_t port) const218 uint32_t get_max_items_per_packet(const size_t port) const 219 { 220 return (_packet_size.at(port).get() - CHDR_MAX_LEN_HDR) 221 / get_play_item_size(port); 222 } 223 get_max_packet_size(const size_t port) const224 uint32_t get_max_packet_size(const size_t port) const 225 { 226 return _packet_size.at(port).get(); 227 } 228 get_play_type(const size_t port) const229 io_type_t get_play_type(const size_t port) const 230 { 231 return _play_type.at(port).get(); 232 } 233 get_play_item_size(const size_t port) const234 size_t get_play_item_size(const size_t port) const 235 { 236 return uhd::convert::get_bytes_per_item(get_play_type(port)); 237 } 238 239 /************************************************************************** 240 * Advanced Record Control API calls 241 *************************************************************************/ set_record_type(const io_type_t type,const size_t port)242 void set_record_type(const io_type_t type, const size_t port) 243 { 244 set_property<std::string>( 245 PROP_KEY_TYPE, type, {res_source_info::INPUT_EDGE, port}); 246 } 247 248 /************************************************************************** 249 * Advanced Playback Control API 250 **************************************************************************/ config_play(const uint64_t offset,const uint64_t size,const size_t port)251 void config_play(const uint64_t offset, const uint64_t size, const size_t port) 252 { 253 set_property<uint64_t>( 254 PROP_KEY_PLAY_OFFSET, offset, {res_source_info::USER, port}); 255 set_property<uint64_t>(PROP_KEY_PLAY_SIZE, size, {res_source_info::USER, port}); 256 _validate_play_buffer(port); 257 } 258 set_play_type(const io_type_t type,const size_t port)259 void set_play_type(const io_type_t type, const size_t port) 260 { 261 set_property<std::string>( 262 PROP_KEY_TYPE, type, {res_source_info::OUTPUT_EDGE, port}); 263 } 264 set_max_items_per_packet(const uint32_t ipp,const size_t port)265 void set_max_items_per_packet(const uint32_t ipp, const size_t port) 266 { 267 set_max_packet_size(CHDR_MAX_LEN_HDR + ipp * get_play_item_size(port), port); 268 } 269 set_max_packet_size(const uint32_t size,const size_t port)270 void set_max_packet_size(const uint32_t size, const size_t port) 271 { 272 set_property<uint32_t>(PROP_KEY_PKT_SIZE, size, {res_source_info::USER, port}); 273 } 274 issue_stream_cmd(const uhd::stream_cmd_t & stream_cmd,const size_t port)275 void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd, const size_t port) 276 { 277 // Ensure that the buffer is properly configured before issuing a stream command 278 _validate_play_buffer(port); 279 RFNOC_LOG_TRACE("replay_block_control_impl::issue_stream_cmd(port=" 280 << port << ", mode=" << char(stream_cmd.stream_mode) << ")"); 281 282 // Setup the mode to instruction flags 283 const uint8_t play_cmd = [stream_cmd]() -> uint8_t { 284 switch (stream_cmd.stream_mode) { 285 case uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS: 286 return PLAY_CMD_STOP; // Stop playing back data 287 case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE: 288 case uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE: 289 return PLAY_CMD_FINITE; // Play NUM_SAMPS then stop 290 case uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS: 291 return PLAY_CMD_CONTINUOUS; // Playback continuously over the play 292 // buffer until stopped 293 default: 294 throw uhd::value_error("Requested invalid stream command."); 295 } 296 }(); 297 298 // Calculate the number of words to transfer in NUM_SAMPS mode 299 if (play_cmd == PLAY_CMD_FINITE) { 300 uint64_t num_words = 301 stream_cmd.num_samps * get_play_item_size(port) / get_word_size(); 302 _replay_reg_iface.poke64(REG_PLAY_CMD_NUM_WORDS_LO_ADDR, num_words, port); 303 } 304 305 // Set the time for the command 306 const uint32_t timed_flag = (stream_cmd.stream_now) ? 0 : PLAY_COMMAND_TIMED_MASK; 307 if (!stream_cmd.stream_now) { 308 const double tick_rate = get_tick_rate(); 309 UHD_LOG_DEBUG("REPLAY", 310 "Using tick rate " << (tick_rate / 1e6) << " MHz to set stream command."); 311 const uint64_t ticks = stream_cmd.time_spec.to_ticks(tick_rate); 312 _replay_reg_iface.poke64(REG_PLAY_CMD_TIME_LO_ADDR, ticks, port); 313 } 314 315 // Issue the stream command 316 uint32_t command_word = (play_cmd & PLAY_COMMAND_MASK) | timed_flag; 317 _replay_reg_iface.poke32(REG_PLAY_CMD_ADDR, command_word, port); 318 } 319 320 protected: 321 // Block-specific register interface 322 multichan_register_iface _replay_reg_iface; 323 324 private: _register_input_props(const size_t port)325 void _register_input_props(const size_t port) 326 { 327 // Get default property values 328 const io_type_t default_type = IO_TYPE_SC16; 329 const uint64_t record_offset = 0; 330 const uint64_t record_size = _mem_size; 331 332 // Initialize properties 333 _record_type.emplace_back(property_t<std::string>( 334 PROP_KEY_TYPE, default_type, {res_source_info::INPUT_EDGE, port})); 335 _record_offset.push_back(property_t<uint64_t>( 336 PROP_KEY_RECORD_OFFSET, record_offset, {res_source_info::USER, port})); 337 _record_size.push_back(property_t<uint64_t>( 338 PROP_KEY_RECORD_SIZE, record_size, {res_source_info::USER, port})); 339 UHD_ASSERT_THROW(_record_type.size() == port + 1); 340 UHD_ASSERT_THROW(_record_offset.size() == port + 1); 341 UHD_ASSERT_THROW(_record_size.size() == port + 1); 342 343 // Register user properties 344 register_property(&_record_type.at(port)); 345 register_property(&_record_offset.at(port)); 346 register_property(&_record_size.at(port)); 347 348 // Add property resolvers 349 add_property_resolver({&_record_offset.at(port)}, {}, [this, port]() { 350 _set_record_offset(_record_offset.at(port).get(), port); 351 }); 352 add_property_resolver({&_record_size.at(port)}, 353 {&_record_size.at(port)}, 354 [this, port]() { _set_record_size(_record_size.at(port).get(), port); }); 355 } 356 _register_output_props(const size_t port)357 void _register_output_props(const size_t port) 358 { 359 // Get default property values 360 const io_type_t default_type = IO_TYPE_SC16; 361 const uint64_t play_offset = 0; 362 const uint64_t play_size = _mem_size; 363 const uint32_t packet_size = get_mtu({res_source_info::OUTPUT_EDGE, port}); 364 365 // Initialize properties 366 _play_type.emplace_back(property_t<std::string>( 367 PROP_KEY_TYPE, default_type, {res_source_info::OUTPUT_EDGE, port})); 368 _play_offset.push_back(property_t<uint64_t>( 369 PROP_KEY_PLAY_OFFSET, play_offset, {res_source_info::USER, port})); 370 _play_size.push_back(property_t<uint64_t>( 371 PROP_KEY_PLAY_SIZE, play_size, {res_source_info::USER, port})); 372 _packet_size.push_back(property_t<uint32_t>( 373 PROP_KEY_PKT_SIZE, packet_size, {res_source_info::USER, port})); 374 UHD_ASSERT_THROW(_play_type.size() == port + 1); 375 UHD_ASSERT_THROW(_play_offset.size() == port + 1); 376 UHD_ASSERT_THROW(_play_size.size() == port + 1); 377 UHD_ASSERT_THROW(_packet_size.size() == port + 1); 378 379 // Register user properties 380 register_property(&_play_type.at(port)); 381 register_property(&_play_offset.at(port)); 382 register_property(&_play_size.at(port)); 383 register_property(&_packet_size.at(port)); 384 385 // Add property resolvers 386 add_property_resolver({&_play_type.at(port)}, {}, [this, port]() { 387 _set_play_type(_play_type.at(port).get(), port); 388 }); 389 add_property_resolver({&_play_offset.at(port)}, {}, [this, port]() { 390 _set_play_offset(_play_offset.at(port).get(), port); 391 }); 392 add_property_resolver({&_play_size.at(port)}, 393 {&_play_size.at(port)}, 394 [this, port]() { _set_play_size(_play_size.at(port).get(), port); }); 395 add_property_resolver({&_packet_size.at(port), 396 get_mtu_prop_ref({res_source_info::OUTPUT_EDGE, port})}, 397 {}, 398 [this, port]() { _set_packet_size(_packet_size.at(port).get(), port); }); 399 } 400 _set_play_type(const io_type_t type,const size_t port)401 void _set_play_type(const io_type_t type, const size_t port) 402 { 403 uint32_t play_item_size = uhd::convert::get_bytes_per_item(type); 404 _replay_reg_iface.poke32(REG_PLAY_ITEM_SIZE_ADDR, play_item_size, port); 405 } 406 _set_record_offset(const uint64_t record_offset,const size_t port)407 void _set_record_offset(const uint64_t record_offset, const size_t port) 408 { 409 if ((record_offset % _word_size) != 0) { 410 throw uhd::value_error("Record offset must be a multiple of word size."); 411 } 412 if (record_offset > _mem_size) { 413 throw uhd::value_error("Record offset is out of bounds."); 414 } 415 _replay_reg_iface.poke64(REG_REC_BASE_ADDR_LO_ADDR, record_offset, port); 416 } 417 _set_record_size(const uint64_t record_size,const size_t port)418 void _set_record_size(const uint64_t record_size, const size_t port) 419 { 420 if ((record_size % _word_size) != 0) { 421 _record_size.at(port) = record_size - (record_size % _word_size); 422 throw uhd::value_error("Record buffer size must be a multiple of word size."); 423 } 424 _replay_reg_iface.poke64(REG_REC_BUFFER_SIZE_LO_ADDR, record_size, port); 425 } 426 _set_play_offset(const uint64_t play_offset,const size_t port)427 void _set_play_offset(const uint64_t play_offset, const size_t port) 428 { 429 if ((play_offset % _word_size) != 0) { 430 throw uhd::value_error("Play offset must be a multiple of word size."); 431 } 432 if (play_offset > _mem_size) { 433 throw uhd::value_error("Play offset is out of bounds."); 434 } 435 _replay_reg_iface.poke64(REG_PLAY_BASE_ADDR_LO_ADDR, play_offset, port); 436 } 437 _set_play_size(const uint64_t play_size,const size_t port)438 void _set_play_size(const uint64_t play_size, const size_t port) 439 { 440 if ((play_size % _word_size) != 0) { 441 _play_size.at(port) = play_size - (play_size % _word_size); 442 throw uhd::value_error("Play buffer size must be a multiple of word size."); 443 } 444 if ((play_size % get_play_item_size(port)) != 0) { 445 _play_size.at(port) = play_size - (play_size % get_play_item_size(port)); 446 throw uhd::value_error("Play buffer size must be a multiple of item size."); 447 } 448 _replay_reg_iface.poke64(REG_PLAY_BUFFER_SIZE_LO_ADDR, play_size, port); 449 } 450 _set_packet_size(const uint32_t packet_size,const size_t port)451 void _set_packet_size(const uint32_t packet_size, const size_t port) 452 { 453 // MTU is max payload size, header with timestamp is already accounted for 454 const size_t mtu = get_mtu({res_source_info::OUTPUT_EDGE, port}); 455 const uint32_t item_size = get_play_item_size(port); 456 const uint32_t mtu_payload = mtu - CHDR_MAX_LEN_HDR; 457 const uint32_t mtu_items = mtu_payload / item_size; 458 const uint32_t ipc = _word_size / item_size; // items per cycle 459 const uint32_t max_ipp_per_mtu = mtu_items - (mtu_items % ipc); 460 const uint32_t payload_size = packet_size - CHDR_MAX_LEN_HDR; 461 uint32_t ipp = payload_size / item_size; 462 if (ipp > max_ipp_per_mtu) { 463 ipp = max_ipp_per_mtu; 464 } 465 if ((ipp % ipc) != 0) { 466 ipp = ipp - (ipp % ipc); 467 RFNOC_LOG_WARNING( 468 "ipp must be a multiple of the block bus width! Coercing to " << ipp); 469 } 470 if (ipp <= 0) { 471 ipp = DEFAULT_SPP; 472 RFNOC_LOG_WARNING("ipp must be greater than zero! Coercing to " << ipp); 473 } 474 // Packet size must be a multiple of word size 475 if ((packet_size % _word_size) != 0) { 476 throw uhd::value_error("Packet size must be a multiple of word size."); 477 } 478 const uint16_t words_per_packet = 479 uhd::narrow_cast<uint16_t>(ipp * item_size / _word_size); 480 _replay_reg_iface.poke32( 481 REG_PLAY_WORDS_PER_PKT_ADDR, uint32_t(words_per_packet), port); 482 } 483 _validate_record_buffer(const size_t port)484 void _validate_record_buffer(const size_t port) 485 { 486 // The entire record buffer must be within the bounds of memory 487 if ((get_record_offset(port) + get_record_size(port)) > get_mem_size()) { 488 throw uhd::value_error("Record buffer goes out of bounds."); 489 } 490 } 491 _validate_play_buffer(const size_t port)492 void _validate_play_buffer(const size_t port) 493 { 494 // Streaming requires that the buffer size is a multiple of item size 495 if ((get_play_size(port) % get_play_item_size(port)) != 0) { 496 throw uhd::value_error("Play size must be must be a multiple of item size."); 497 } 498 // The entire play buffer must be within the bounds of memory 499 if ((get_play_offset(port) + get_play_size(port)) > get_mem_size()) { 500 throw uhd::value_error("Play buffer goes out of bounds."); 501 } 502 } 503 504 /************************************************************************** 505 * Attributes 506 *************************************************************************/ 507 const size_t _num_input_ports; 508 const size_t _num_output_ports; 509 510 // Block compat number 511 const uint32_t _fpga_compat; 512 513 // These size params are configurable in the FPGA 514 const uint16_t _word_size; 515 const uint64_t _mem_size; 516 517 std::vector<property_t<std::string>> _record_type; 518 std::vector<property_t<uint64_t>> _record_offset; 519 std::vector<property_t<uint64_t>> _record_size; 520 std::vector<property_t<std::string>> _play_type; 521 std::vector<property_t<uint64_t>> _play_offset; 522 std::vector<property_t<uint64_t>> _play_size; 523 std::vector<property_t<uint32_t>> _packet_size; 524 }; 525 526 UHD_RFNOC_BLOCK_REGISTER_DIRECT( 527 replay_block_control, REPLAY_BLOCK, "Replay", CLOCK_KEY_GRAPH, "bus_clk") 528