1// 2// Copyright 2019 Ettus Research, A National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: PkgRfnocBlockCtrlBfm 7// 8// Description: This package includes a high-level bus functional model (BFM) 9// for a block controller. This models a software block controller and allows 10// communication with a single RFNoC block. It includes the following: 11// 12// - A CtrlIfaceBfm for an AXIS-Ctrl port connection of a block 13// - One or more ChdrIfaceBfm for the AXIS-CHDR port connections 14// - A connection for the backend interface of a block 15// 16 17 18//----------------------------------------------------------------------------- 19// SV Interface for the RFNoC Backend Iface 20//----------------------------------------------------------------------------- 21 22typedef struct packed { 23 bit [476:0] reserved0; 24 bit soft_chdr_rst; 25 bit soft_ctrl_rst; 26 bit flush_en; 27 bit [31:0] flush_timeout; 28} backend_config_v1_t; 29 30typedef struct packed { 31 bit [439:0] reserved0; 32 bit [5:0] mtu; 33 bit flush_done; 34 bit flush_active; 35 bit [31:0] noc_id; 36 bit [7:0] ctrl_max_async_msgs; 37 bit [5:0] ctrl_fifosize; 38 bit [5:0] num_data_o; 39 bit [5:0] num_data_i; 40 bit [5:0] proto_ver; 41} backend_status_v1_t; 42 43typedef union packed { 44 backend_config_v1_t v1; 45} backend_config_t; 46 47typedef union packed { 48 backend_status_v1_t v1; 49} backend_status_t; 50 51 52interface RfnocBackendIf( 53 input logic chdr_clk, 54 input logic ctrl_clk 55); 56 backend_config_t cfg; 57 backend_status_t sts; 58 59 modport master ( 60 input chdr_clk, 61 input ctrl_clk, 62 output cfg, 63 input sts 64 ); 65 modport slave ( 66 input chdr_clk, 67 input ctrl_clk, 68 input cfg, 69 output sts 70 ); 71endinterface : RfnocBackendIf 72 73 74//----------------------------------------------------------------------------- 75// RFNoC Block Controller Bus Functional Model 76//----------------------------------------------------------------------------- 77 78package PkgRfnocBlockCtrlBfm; 79 80 import PkgChdrUtils::*; 81 import PkgChdrBfm::*; 82 import PkgCtrlIfaceBfm::*; 83 import PkgChdrIfaceBfm::*; 84 85 export PkgChdrBfm::ChdrPacket; 86 export PkgCtrlIfaceBfm::CtrlIfaceBfm; 87 export PkgChdrIfaceBfm::ChdrIfaceBfm; 88 export PkgChdrIfaceBfm::packet_info_t; 89 export PkgChdrIfaceBfm::packet_info_equal; 90 91 92 //--------------------------------------------------------------------------- 93 // Block Controller BFM 94 //--------------------------------------------------------------------------- 95 // 96 // This class models a block controller in software 97 // 98 //--------------------------------------------------------------------------- 99 100 class RfnocBlockCtrlBfm #(CHDR_W = 64, ITEM_W = 32); 101 102 local virtual RfnocBackendIf.master backend; 103 local CtrlIfaceBfm ctrl; 104 local ChdrIfaceBfm #(CHDR_W, ITEM_W) m_data[$]; 105 local ChdrIfaceBfm #(CHDR_W, ITEM_W) s_data[$]; 106 local bit running; 107 108 localparam CMD_PROP_CYC = 5; 109 110 typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; 111 typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; 112 113 // Class constructor to create a new BFM instance. 114 // 115 // backend: Interface for the backend signals of a block 116 // m_ctrl: Interface for the CTRL master connection (EP's AXIS-Ctrl output) 117 // s_ctrl: Interface for the CTRL slave connection (EP's AXIS-Ctrl input) 118 // dst_port: Destination port to use in generated control packets 119 // src_port: Source port to use in generated control packets 120 // 121 function new( 122 virtual RfnocBackendIf.master backend, 123 virtual AxiStreamIf #(32).master m_ctrl, 124 virtual AxiStreamIf #(32).slave s_ctrl, 125 input ctrl_port_t dst_port = 10'd2, 126 input ctrl_port_t src_port = 10'd1 127 ); 128 this.backend = backend; 129 this.ctrl = new(m_ctrl, s_ctrl, dst_port, src_port); 130 this.running = 0; 131 endfunction : new 132 133 // Add a master data port. This should connect to a DUT slave input. 134 // 135 // m_chdr: Virtual master interface to connect new port to. 136 // max_payload_length: Maximum payload length (in bytes) to create when 137 // building packets from data. 138 // ticks_per_word: Number of timebase clock ticks to increment per 139 // CHDR word. 140 // 141 function int add_master_data_port( 142 virtual AxiStreamIf #(CHDR_W).master m_chdr, 143 int max_payload_length = 2**$bits(chdr_length_t), 144 int ticks_per_word = CHDR_W/ITEM_W 145 ); 146 ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = 147 new(m_chdr, null, max_payload_length, ticks_per_word); 148 m_data.push_back(bfm); 149 return m_data.size() - 1; 150 endfunction : add_master_data_port 151 152 // Add a slave data port. This should connect to a DUT master output. 153 // 154 // s_chdr: Virtual slave interface to connect new port to 155 // 156 function int add_slave_data_port( 157 virtual AxiStreamIf #(CHDR_W).slave s_chdr 158 ); 159 ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = new(null, s_chdr); 160 s_data.push_back(bfm); 161 return s_data.size() - 1; 162 endfunction : add_slave_data_port 163 164 // Add a master data port. This is equivalent to add_master_data_port() 165 // except it accepts a port number and it waits until the preceding ports 166 // are connected to ensure that ports are connected in the correct order. 167 // 168 // port_num: The port number to which m_chdr should be connected 169 // m_chdr: Master CHDR interface to connect to the port 170 // max_payload_length: Maximum payload length (in bytes) to create when 171 // building packets from data. 172 // ticks_per_word: Number of timebase clock ticks to increment per 173 // CHDR word. 174 // 175 task connect_master_data_port( 176 int port_num, 177 virtual AxiStreamIf #(CHDR_W).master m_chdr, 178 int max_payload_length = 2**$bits(chdr_length_t), 179 int ticks_per_word = CHDR_W/ITEM_W 180 ); 181 ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = 182 new(m_chdr, null, max_payload_length, ticks_per_word); 183 wait (m_data.size() == port_num); 184 m_data.push_back(bfm); 185 endtask : connect_master_data_port 186 187 // Add a slave data port. This is equivalent to add_slave_data_port() 188 // except it accepts a port number and it waits until the preceding ports 189 // are connected to ensure that ports are connected in the correct order. 190 // 191 // port_num: The port number to which m_chdr should be connected 192 // s_chdr: Master CHDR interface to connect to the port 193 // 194 task connect_slave_data_port( 195 int port_num, 196 virtual AxiStreamIf #(CHDR_W).slave s_chdr 197 ); 198 ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = new(null, s_chdr); 199 wait (s_data.size() == port_num); 200 s_data.push_back(bfm); 201 endtask : connect_slave_data_port 202 203 // Start the data and control BFM's processes running. 204 task run(); 205 assert (backend.sts.v1.proto_ver == 1) else begin 206 $fatal(1, "The connected block has an incompatible backend interface"); 207 end 208 if (!running) begin 209 ctrl.run(); 210 foreach (m_data[i]) 211 m_data[i].run(); 212 foreach (s_data[i]) 213 s_data[i].run(); 214 running = 1; 215 end 216 endtask : run 217 218 // Return a handle to the control BFM 219 function CtrlIfaceBfm get_ctrl_bfm(); 220 return ctrl; 221 endfunction : get_ctrl_bfm 222 223 // Return a handle to the indicated master port BFM 224 function ChdrIfaceBfm #(CHDR_W, ITEM_W) get_master_data_bfm(int port); 225 assert (port >= 0 && port < m_data.size()) else begin 226 $fatal(1, "Invalid master port number"); 227 end 228 return m_data[port]; 229 endfunction : get_master_data_bfm 230 231 // Return a handle to the indicated slave port BFM 232 function ChdrIfaceBfm #(CHDR_W, ITEM_W) get_slave_data_bfm(int port); 233 assert (port >= 0 && port < s_data.size()) else begin 234 $fatal(1, "Invalid slave port number"); 235 end 236 return s_data[port]; 237 endfunction : get_slave_data_bfm 238 239 // Set the maximum payload size for packets. This value is used to split 240 // large send requests across multiple packets. 241 // 242 // port: Master port whose maximum length you want to set 243 // max_length: Maximum payload length in bytes for each packet 244 // 245 function void set_max_payload_length(int port, int max_length); 246 assert (port >= 0 && port < m_data.size()) else begin 247 $fatal(1, "Invalid master port number"); 248 end 249 m_data[port].set_max_payload_length(max_length); 250 endfunction 251 252 // Return the maximum payload size for packets. This value is used to split 253 // large send requests across multiple packets. 254 // 255 // port: Master port whose maximum length you want to get 256 // 257 function int get_max_payload_length(int port); 258 assert (port >= 0 && port < m_data.size()) else begin 259 $fatal(1, "Invalid master port number"); 260 end 261 return m_data[port].get_max_payload_length(); 262 endfunction 263 264 // Set the timestamp ticks per CHDR_W sized word. 265 // 266 // port: Master port whose timestamp increment you want to set 267 // ticks_per_word: Amount to increment the timestamp per CHDR_W sized word 268 // 269 function void set_ticks_per_word(int port, int ticks_per_word); 270 assert (port >= 0 && port < m_data.size()) else begin 271 $fatal(1, "Invalid master port number"); 272 end 273 m_data[port].set_ticks_per_word(ticks_per_word); 274 endfunction 275 276 // Return the timestamp ticks per CHDR_W sized word. 277 // 278 // port: Master port whose timestamp increment you want to get 279 // 280 function int get_ticks_per_word(int port); 281 assert (port >= 0 && port < m_data.size()) else begin 282 $fatal(1, "Invalid master port number"); 283 end 284 return m_data[port].get_ticks_per_word(); 285 endfunction 286 287 // Get static info about the block 288 function logic [7:0] get_proto_ver(); 289 return backend.sts.v1.proto_ver; 290 endfunction : get_proto_ver 291 292 function logic [31:0] get_noc_id(); 293 return backend.sts.v1.noc_id; 294 endfunction : get_noc_id 295 296 function logic [5:0] get_num_data_i(); 297 return backend.sts.v1.num_data_i; 298 endfunction : get_num_data_i 299 300 function logic [5:0] get_num_data_o(); 301 return backend.sts.v1.num_data_o; 302 endfunction : get_num_data_o 303 304 function logic [5:0] get_ctrl_fifosize(); 305 return backend.sts.v1.ctrl_fifosize; 306 endfunction : get_ctrl_fifosize 307 308 function logic [5:0] get_mtu(); 309 return backend.sts.v1.mtu; 310 endfunction : get_mtu 311 312 // Soft-Reset the CHDR path 313 // 314 // rst_cyc: Number of cycles to wait for reset completion 315 // 316 task reset_chdr(input int rst_cyc = 100); 317 assert (running) else begin 318 $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running"); 319 end 320 321 // Assert soft_chdr_rst then wait 322 // Note: soft_chdr_rst must be driven in the ctrl_clk domain 323 @(posedge backend.ctrl_clk); 324 backend.cfg.v1.soft_chdr_rst = 1; 325 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 326 backend.cfg.v1.soft_chdr_rst = 0; 327 @(posedge backend.ctrl_clk); 328 repeat (rst_cyc) @(posedge backend.ctrl_clk); 329 endtask : reset_chdr 330 331 // Soft-Reset the Control path 332 // 333 // rst_cyc: Number of cycles to wait for reset completion 334 // 335 task reset_ctrl(input int rst_cyc = 100); 336 assert (running) else begin 337 $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running"); 338 end 339 340 // Assert soft_ctrl_rst then wait 341 @(posedge backend.ctrl_clk); 342 backend.cfg.v1.soft_ctrl_rst = 1; 343 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 344 backend.cfg.v1.soft_ctrl_rst = 0; 345 repeat (rst_cyc) @(posedge backend.ctrl_clk); 346 endtask : reset_ctrl 347 348 // Flush the data ports of the block 349 // 350 // idle_cyc: Number of idle cycles before done is asserted 351 // 352 task flush(input logic [31:0] idle_cyc = 100); 353 assert (running) else begin 354 $fatal(1, "Cannot call flush until RfnocBlockCtrlBfm is running"); 355 end 356 357 // Set flush timeout then wait 358 backend.cfg.v1.flush_timeout = idle_cyc; 359 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 360 repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk); 361 // Start flush then wait for done 362 @(posedge backend.ctrl_clk); 363 backend.cfg.v1.flush_en = 1; 364 @(posedge backend.ctrl_clk); 365 while (~backend.sts.v1.flush_done) @(posedge backend.ctrl_clk); 366 // Deassert flush then wait 367 backend.cfg.v1.flush_en = 0; 368 while (backend.sts.v1.flush_active) @(posedge backend.ctrl_clk); 369 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 370 repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk); 371 endtask : flush 372 373 // Flush the data ports of the block then reset the CHDR 374 // path, wait then reset the ctrl path 375 // 376 // idle_cyc: Number of idle cycles before done is asserted 377 // chdr_rst_cyc: Number of cycles to wait for chdr_rst completion 378 // ctrl_rst_cyc: Number of cycles to wait for ctrl_rst completion 379 // 380 task flush_and_reset( 381 input logic [31:0] idle_cyc = 100, 382 input int chdr_rst_cyc = 100, 383 input int ctrl_rst_cyc = 100 384 ); 385 assert (running) else begin 386 $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running"); 387 end 388 389 // Set flush timeout then wait 390 backend.cfg.v1.flush_timeout = idle_cyc; 391 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 392 repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk); 393 // Start flush then wait for done 394 @(posedge backend.ctrl_clk); 395 backend.cfg.v1.flush_en = 1; 396 @(posedge backend.ctrl_clk); 397 while (~backend.sts.v1.flush_done) @(posedge backend.ctrl_clk); 398 // Assert chdr_rst then wait 399 reset_chdr(chdr_rst_cyc); 400 // Assert ctrl_rst then wait 401 reset_ctrl(ctrl_rst_cyc); 402 // Deassert flush then wait 403 backend.cfg.v1.flush_en = 0; 404 while (backend.sts.v1.flush_active) @(posedge backend.ctrl_clk); 405 repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk); 406 repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk); 407 endtask : flush_and_reset 408 409 410 // Send a CHDR data packet out the CHDR data interface. 411 // 412 // port: Port to send the CHDR packet on. 413 // data: Data words to insert into the CHDR packet. 414 // data_bytes: Size of data in bytes. If omitted or -1, data_bytes will 415 // be calculated based on the number of words in data. 416 // metadata: Metadata words to insert into the CHDR packet. Omit this 417 // argument (or set to an empty array) to not include 418 // metadata. 419 // pkt_info: Data structure containing packet header information. 420 // 421 task send( 422 input int port, 423 input chdr_word_t data[$], 424 input int data_bytes = -1, 425 input chdr_word_t metadata[$] = {}, 426 input packet_info_t pkt_info = 0 427 ); 428 assert (running) else begin 429 $fatal(1, "Cannot call send until RfnocBlockCtrlBfm is running"); 430 end 431 assert (port >= 0 && port < m_data.size()) else begin 432 $fatal(1, "Invalid master port number"); 433 end 434 435 m_data[port].send(data, data_bytes, metadata, pkt_info); 436 endtask : send 437 438 439 // Send a CHDR data packet, filling the payload with items. 440 // 441 // port: Port to send the CHDR packet on. 442 // items: Data items to insert into the CHDR packet's payload. 443 // metadata: Metadata words to insert into the CHDR packet. Omit this 444 // argument (or set to an empty array) to not include 445 // metadata. 446 // pkt_info: Data structure containing packet header information. 447 // 448 task send_items( 449 input int port, 450 input item_t items[$], 451 input chdr_word_t metadata[$] = {}, 452 input packet_info_t pkt_info = 0 453 ); 454 assert (running) else begin 455 $fatal(1, "Cannot call send_items until RfnocBlockCtrlBfm is running"); 456 end 457 assert (port >= 0 && port < m_data.size()) else begin 458 $fatal(1, "Invalid master port number"); 459 end 460 461 m_data[port].send_items(items, metadata, pkt_info); 462 endtask : send_items 463 464 465 // Send data as one or more CHDR data packets. The input data and metadata 466 // is automatically broken into max_payload_length'd packets. The 467 // timestamp, if present, is set for the first packet and updated for 468 // subsequent packets. The EOB and EOV are only applied to the last packet. 469 // 470 // port: Port to send the CHDR packet(s) on. 471 // data: Data words to insert into the CHDR packets. 472 // data_bytes: Size of data in bytes. If omitted or -1, data_bytes will 473 // be calculated based on the number of words in data. 474 // metadata: Metadata words to insert into the CHDR packet(s). Omit 475 // this argument (or set to an empty array) to not include 476 // metadata. 477 // pkt_info: Data structure containing packet header information. 478 // 479 task send_packets( 480 input int port, 481 input chdr_word_t data[$], 482 input int data_bytes = -1, 483 input chdr_word_t metadata[$] = {}, 484 input packet_info_t pkt_info = 0 485 ); 486 assert (running) else begin 487 $fatal(1, "Cannot call send_packets until RfnocBlockCtrlBfm is running"); 488 end 489 assert (port >= 0 && port < m_data.size()) else begin 490 $fatal(1, "Invalid master port number"); 491 end 492 493 m_data[port].send_packets(data, data_bytes, metadata, pkt_info); 494 endtask : send_packets 495 496 497 // Send one or more CHDR data packets, filling the payload with items. The 498 // input data and metadata is automatically broken into 499 // max_payload_length'd packets. The timestamp, if present, is set for the 500 // first packet and updated for subsequent packets. The EOB and EOV are 501 // only applied to the last packet. 502 // 503 // port: Port to send the CHDR packet(s) on. 504 // items: Data items to insert into the payload of the CHDR packets. 505 // metadata: Metadata words to insert into the CHDR packet(s). Omit 506 // this argument (or set to an empty array) to not include 507 // metadata. 508 // pkt_info: Data structure containing packet header information. 509 // 510 task send_packets_items( 511 input int port, 512 input item_t items[$], 513 input chdr_word_t metadata[$] = {}, 514 input packet_info_t pkt_info = 0 515 ); 516 assert (running) else begin 517 $fatal(1, "Cannot call send_packets_items until RfnocBlockCtrlBfm is running"); 518 end 519 assert (port >= 0 && port < m_data.size()) else begin 520 $fatal(1, "Invalid master port number"); 521 end 522 523 m_data[port].send_packets_items(items, metadata, pkt_info); 524 endtask : send_packets_items 525 526 527 // Receive a CHDR data packet on the CHDR data interface and extract its 528 // contents. 529 // 530 // port: Port to receive the CHDR packet from. 531 // data: Data words from the received CHDR packet. 532 // data_bytes: The number of data bytes in the CHDR packet. This 533 // is useful if the data is not a multiple of the 534 // chdr_word_t size. 535 // metadata: Metadata words from the received CHDR packet. This 536 // will be an empty array if there was no metadata. 537 // pkt_info: Data structure to receive packet header information. 538 // 539 task recv_adv( 540 input int port, 541 output chdr_word_t data[$], 542 output int data_bytes, 543 output chdr_word_t metadata[$], 544 output packet_info_t pkt_info 545 ); 546 assert (running) else begin 547 $fatal(1, "Cannot call recv_adv until RfnocBlockCtrlBfm is running"); 548 end 549 assert (port >= 0 && port < s_data.size()) else begin 550 $fatal(1, "Invalid slave port number"); 551 end 552 553 s_data[port].recv_adv(data, data_bytes, metadata, pkt_info); 554 endtask : recv_adv 555 556 557 // Receive a CHDR data packet on the CHDR data interface and extract its 558 // contents, putting the payload into a queue of items. 559 // 560 // port: Port to receive the CHDR packet from. 561 // items: Items extracted from the payload of the received packet. 562 // metadata: Metadata words from the received CHDR packet. This 563 // will be an empty array if there was no metadata. 564 // pkt_info: Data structure to receive packet header information. 565 // 566 task recv_items_adv( 567 input int port, 568 output item_t items[$], 569 output chdr_word_t metadata[$], 570 output packet_info_t pkt_info 571 ); 572 assert (running) else begin 573 $fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running"); 574 end 575 assert (port >= 0 && port < s_data.size()) else begin 576 $fatal(1, "Invalid slave port number"); 577 end 578 579 s_data[port].recv_items_adv(items, metadata, pkt_info); 580 endtask : recv_items_adv 581 582 583 // Receive a CHDR data packet on the CHDR data interface and extract the 584 // data. Any metadata or timestamp, if present, are discarded. 585 // 586 // port: Port number for the block to receive from 587 // data: Data words from the received CHDR packet 588 // data_bytes: The number of data bytes in the CHDR packet. This 589 // is useful if the data is not a multiple of the 590 // chdr_word_t size. 591 // 592 task recv( 593 input int port, 594 output chdr_word_t data[$], 595 output int data_bytes 596 ); 597 assert (running) else begin 598 $fatal(1, "Cannot call recv until RfnocBlockCtrlBfm is running"); 599 end 600 assert (port >= 0 && port < s_data.size()) else begin 601 $fatal(1, "Invalid slave port number"); 602 end 603 604 s_data[port].recv(data, data_bytes); 605 endtask : recv 606 607 608 // Receive a CHDR data packet on the CHDR data interface and extract its 609 // payload into a queue of items. Any metadata or timestamp, if present, 610 // are discarded. 611 // 612 // port: Port number for the block to receive from. 613 // items: Data items extracted from payload of the received packet. 614 // data_bytes: The number of data bytes in the CHDR packet. This 615 // is useful if the data is not a multiple of the 616 // chdr_word_t size. 617 // 618 task recv_items( 619 input int port, 620 output item_t items[$] 621 ); 622 assert (running) else begin 623 $fatal(1, "Cannot call recv_items until RfnocBlockCtrlBfm is running"); 624 end 625 assert (port >= 0 && port < s_data.size()) else begin 626 $fatal(1, "Invalid slave port number"); 627 end 628 629 s_data[port].recv_items(items); 630 endtask : recv_items 631 632 633 // Receive one ore more CHDR data packets and extract their contents, 634 // putting the payload into a queue of items. Any metadata or timestamp, if 635 // present, are discarded. 636 // 637 // port: Port number for the block to receive from. 638 // items: Items extracted from the payload of the received packets. 639 // num_items: (Optional) Minimum number of items to receive. This must be 640 // provided, unless eob or eov are set. Defaults to -1, which 641 // means that the number of items is not limited. 642 // eob: (Optional) Receive up until the next End of Burst (EOB). 643 // Default value is 1, so an entire burst is received. 644 // eov: (Optional) Receive up until the next End of Vector (EOV). 645 // 646 task recv_packets_items( 647 input int port, 648 output item_t items[$], 649 input int num_items = -1, // Receive a full burst by default 650 input bit eob = 1, 651 input bit eov = 0 652 ); 653 assert (running) else begin 654 $fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running"); 655 end 656 assert (port >= 0 && port < s_data.size()) else begin 657 $fatal(1, "Invalid slave port number"); 658 end 659 660 s_data[port].recv_packets_items( 661 items, num_items, eob, eov); 662 endtask : recv_packets_items 663 664 665 // Receive one or more CHDR data packets and extract their contents, 666 // putting the payload into a queue of items and the metadata into a queue 667 // of CHDR words. 668 // 669 // port: Port number for the block to receive from. 670 // items: Items extracted from the payload of the received packets. 671 // metadata: Metadata words from the received CHDR packets. This will be 672 // an empty array if there was no metadata. 673 // pkt_info: Data structure to receive packet information. The 674 // timestamp, if present, will correspond to the time of the 675 // first sample, whereas vc/eob/eov will correspond to the 676 // state of the last packet. 677 // num_items: (Optional) Minimum number of items to receive. This must be 678 // provided, unless eob or eov are set. Defaults to -1, which 679 // means that the number of items is not limited. 680 // eob: (Optional) Receive up until the next End of Burst (EOB). 681 // Default value is 1, so an entire burst is received. 682 // eov: (Optional) Receive up until the next End of Vector (EOV). 683 // 684 task recv_packets_items_adv( 685 input int port, 686 output item_t items[$], 687 output chdr_word_t metadata[$], 688 output packet_info_t pkt_info, 689 input int num_items = -1, // Receive a full burst by default 690 input bit eob = 1, 691 input bit eov = 0 692 ); 693 assert (running) else begin 694 $fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running"); 695 end 696 assert (port >= 0 && port < s_data.size()) else begin 697 $fatal(1, "Invalid slave port number"); 698 end 699 700 s_data[port].recv_packets_items_adv( 701 items, metadata, pkt_info, num_items, eob, eov); 702 endtask : recv_packets_items_adv 703 704 705 // Transmit a raw CHDR packet. 706 // 707 // port: Port number on which to transmit the packet 708 // packet: Packet to transmit 709 // 710 task put_chdr( 711 input int port, 712 input ChdrPacket #(CHDR_W) packet 713 ); 714 assert (running) else begin 715 $fatal(1, "Cannot call put_chdr until RfnocBlockCtrlBfm is running"); 716 end 717 assert (port >= 0 && port < m_data.size()) else begin 718 $fatal(1, "Invalid master port number"); 719 end 720 721 m_data[port].put_chdr(packet); 722 endtask : put_chdr 723 724 725 // Receive a raw CHDR packet. 726 // 727 // port: Port number on which to receive the packet 728 // packet: Data structure to store received packet 729 // 730 task get_chdr( 731 input int port, 732 output ChdrPacket #(CHDR_W) packet 733 ); 734 assert (running) else begin 735 $fatal(1, "Cannot call get_chdr until RfnocBlockCtrlBfm is running"); 736 end 737 assert (port >= 0 && port < s_data.size()) else begin 738 $fatal(1, "Invalid slave port number"); 739 end 740 741 s_data[port].get_chdr(packet); 742 endtask : get_chdr 743 744 745 // Receive a raw CHDR packet, but don't remove it from the receive queue. 746 // 747 // port: Port number on which to peek 748 // packet: Data structure to store received packet 749 // 750 task peek_chdr( 751 input int port, 752 output ChdrPacket #(CHDR_W) packet 753 ); 754 assert (running) else begin 755 $fatal(1, "Cannot call peek_chdr until RfnocBlockCtrlBfm is running"); 756 end 757 assert (port >= 0 && port < s_data.size()) else begin 758 $fatal(1, "Invalid slave port number"); 759 end 760 761 s_data[port].peek_chdr(packet); 762 endtask : peek_chdr 763 764 765 // Return the number of packets available in the receive queue for the 766 // given port. 767 // 768 // port: Port for which to get the number of received packets 769 // 770 function int num_received(int port); 771 assert (port >= 0 && port < s_data.size()) else begin 772 $fatal(1, "Invalid slave port number"); 773 end 774 775 return s_data[port].num_received(); 776 endfunction 777 778 779 // Wait until packets have completed transmission. 780 // 781 // port: Port for which to wait 782 // num: Number of packets to wait for. Set to -1 or omit the argument 783 // to wait for all currently queued packets to complete 784 // transmission. 785 // 786 task wait_complete(int port, int num = -1); 787 assert (running) else begin 788 $fatal(1, "Cannot call wait_complete until RfnocBlockCtrlBfm is running"); 789 end 790 assert (port >= 0 && port < m_data.size()) else begin 791 $fatal(1, "Invalid master port number"); 792 end 793 794 m_data[port].wait_complete(num); 795 endtask 796 797 798 // Set the stall probability for the indicated slave port. 799 // 800 // port: Port for which to set the probability 801 // stall_prob: Probability as a percentage (0-100) 802 // 803 function void set_slave_stall_prob(int port, int stall_prob); 804 assert (port >= 0 && port < s_data.size()) else begin 805 $fatal(1, "Invalid slave port number"); 806 end 807 808 s_data[port].set_slave_stall_prob(stall_prob); 809 endfunction 810 811 812 // Set the stall probability for the indicated master port. 813 // 814 // port: Port for which to set the probability 815 // stall_prob: Probability as a percentage (0-100) 816 // 817 function void set_master_stall_prob(int port, int stall_prob); 818 assert (port >= 0 && port < m_data.size()) else begin 819 $fatal(1, "Invalid master port number"); 820 end 821 822 m_data[port].set_master_stall_prob(stall_prob); 823 endfunction 824 825 826 // Send a read request packet on the AXIS-Ctrl interface and get the 827 // response. 828 // 829 // addr: Address for the read request 830 // word: Data word that was returned in response to the read 831 // 832 task reg_read( 833 input ctrl_address_t addr, 834 output ctrl_word_t word 835 ); 836 assert (running) else begin 837 $fatal(1, "Cannot call reg_read until RfnocBlockCtrlBfm is running"); 838 end 839 840 ctrl.reg_read(addr, word); 841 endtask : reg_read 842 843 844 // Send a a write request packet on the AXIS-Ctrl interface and get the 845 // response. 846 // 847 // addr: Address for the write request 848 // word: Data word to write 849 // 850 task reg_write( 851 ctrl_address_t addr, 852 ctrl_word_t word 853 ); 854 assert (running) else begin 855 $fatal(1, "Cannot call reg_write until RfnocBlockCtrlBfm is running"); 856 end 857 858 ctrl.reg_write(addr, word); 859 endtask : reg_write 860 861 // Compare data vectors 862 static function bit compare_data( 863 input chdr_word_t lhs[$], 864 input chdr_word_t rhs[$], 865 input int bytes = -1 866 ); 867 int bytes_left; 868 if (lhs.size() != rhs.size()) return 0; 869 bytes_left = (bytes > 0) ? bytes : ((lhs.size()*$size(chdr_word_t))/8); 870 for (int i = 0; i < lhs.size(); i++) begin 871 chdr_word_t mask = {$size(chdr_word_t){1'b1}}; 872 if (bytes_left < $size(chdr_word_t)/8) begin 873 mask = (1 << (bytes_left * 8)) - 1; 874 end else if (bytes_left < 0) begin 875 return 1; 876 end 877 if ((lhs[i] & mask) != (rhs[i] & mask)) return 0; 878 end 879 return 1; 880 endfunction : compare_data 881 882 endclass : RfnocBlockCtrlBfm 883 884 885endpackage : PkgRfnocBlockCtrlBfm 886