1// 2// Copyright 2019 Ettus Research, a National Instruments Company 3// 4// SPDX-License-Identifier: LGPL-3.0-or-later 5// 6// Module: rfnoc_block_duc_tb 7// 8// Description: Testbench for rfnoc_block_duc 9// 10 11 12module rfnoc_block_duc_tb(); 13 14 // Include macros and time declarations for use with PkgTestExec 15 `include "test_exec.svh" 16 17 import PkgTestExec::*; 18 import PkgChdrUtils::*; 19 import PkgRfnocBlockCtrlBfm::*; 20 21 `include "rfnoc_block_duc_regs.vh" 22 23 24 //--------------------------------------------------------------------------- 25 // Local Parameters 26 //--------------------------------------------------------------------------- 27 28 // Simulation parameters 29 localparam real CHDR_CLK_PER = 5.0; // CHDR clock rate 30 localparam real DUC_CLK_PER = 4.0; // DUC IP clock rate 31 localparam int EXTENDED_TEST = 0; // Perform a longer test 32 localparam int SPP = 128; // Samples per packet 33 localparam int PKT_SIZE_BYTES = SPP*4; // Bytes per packet 34 localparam int STALL_PROB = 25; // BFM stall probability 35 36 // Block configuration 37 localparam int CHDR_W = 64; 38 localparam int SAMP_W = 32; 39 localparam int THIS_PORTID = 'h123; 40 localparam int MTU = 8; 41 localparam int NUM_PORTS = 1; 42 localparam int NUM_HB = 3; 43 localparam int CIC_MAX_INTERP = 128; 44 localparam int NOC_ID = 32'hD0C00000; 45 46 47 //--------------------------------------------------------------------------- 48 // Clocks 49 //--------------------------------------------------------------------------- 50 51 bit rfnoc_chdr_clk; 52 bit rfnoc_ctrl_clk; 53 bit ce_clk; 54 55 sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); 56 sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); 57 sim_clock_gen #(DUC_CLK_PER) duc_clk_gen (.clk(ce_clk), .rst()); 58 59 60 //--------------------------------------------------------------------------- 61 // Bus Functional Models 62 //--------------------------------------------------------------------------- 63 64 typedef ChdrData #(CHDR_W, SAMP_W)::chdr_word_t chdr_word_t; 65 66 RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); 67 AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); 68 AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); 69 AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); 70 AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); 71 72 // Bus functional model for a software block controller 73 RfnocBlockCtrlBfm #(CHDR_W, SAMP_W) blk_ctrl = 74 new(backend, m_ctrl, s_ctrl); 75 76 // Connect block controller to BFMs 77 for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections 78 initial begin 79 blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES); 80 blk_ctrl.connect_slave_data_port(i, s_chdr[i]); 81 blk_ctrl.set_master_stall_prob(i, STALL_PROB); 82 blk_ctrl.set_slave_stall_prob(i, STALL_PROB); 83 end 84 end 85 86 87 //--------------------------------------------------------------------------- 88 // DUT 89 //--------------------------------------------------------------------------- 90 91 logic [NUM_PORTS*CHDR_W-1:0] s_rfnoc_chdr_tdata; 92 logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tlast; 93 logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tvalid; 94 logic [ NUM_PORTS-1:0] s_rfnoc_chdr_tready; 95 96 logic [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata; 97 logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast; 98 logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid; 99 logic [ NUM_PORTS-1:0] m_rfnoc_chdr_tready; 100 101 // Map the array of BFMs to a flat vector for the DUT 102 genvar i; 103 for (i = 0; i < NUM_PORTS; i++) begin : gen_dut_connections 104 // Connect BFM master to DUT slave port 105 assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata; 106 assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast; 107 assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid; 108 assign m_chdr[i].tready = s_rfnoc_chdr_tready[i]; 109 110 // Connect BFM slave to DUT master port 111 assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W]; 112 assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i]; 113 assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i]; 114 assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready; 115 end 116 117 rfnoc_block_duc #( 118 .THIS_PORTID (THIS_PORTID), 119 .CHDR_W (CHDR_W), 120 .NUM_PORTS (NUM_PORTS), 121 .MTU (MTU), 122 .NUM_HB (NUM_HB), 123 .CIC_MAX_INTERP (CIC_MAX_INTERP) 124 ) rfnoc_block_duc_i ( 125 .rfnoc_chdr_clk (backend.chdr_clk), 126 .ce_clk (ce_clk), 127 .s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata), 128 .s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast), 129 .s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid), 130 .s_rfnoc_chdr_tready (s_rfnoc_chdr_tready), 131 .m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata), 132 .m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast), 133 .m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid), 134 .m_rfnoc_chdr_tready (m_rfnoc_chdr_tready), 135 .rfnoc_core_config (backend.cfg), 136 .rfnoc_core_status (backend.sts), 137 .rfnoc_ctrl_clk (backend.ctrl_clk), 138 .s_rfnoc_ctrl_tdata (m_ctrl.tdata), 139 .s_rfnoc_ctrl_tlast (m_ctrl.tlast), 140 .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid), 141 .s_rfnoc_ctrl_tready (m_ctrl.tready), 142 .m_rfnoc_ctrl_tdata (s_ctrl.tdata), 143 .m_rfnoc_ctrl_tlast (s_ctrl.tlast), 144 .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid), 145 .m_rfnoc_ctrl_tready (s_ctrl.tready) 146 ); 147 148 149 //--------------------------------------------------------------------------- 150 // Helper Tasks 151 //--------------------------------------------------------------------------- 152 153 // Translate the desired register access to a ctrlport write request. 154 task automatic write_reg(int port, byte unsigned addr, bit [31:0] value); 155 blk_ctrl.reg_write(256*8*port + addr*8, value); 156 endtask : write_reg 157 158 159 // Translate the desired register access to a ctrlport read request. 160 task automatic read_user_reg(int port, byte unsigned addr, output logic [63:0] value); 161 blk_ctrl.reg_read(256*8*port + addr*8 + 0, value[31: 0]); 162 blk_ctrl.reg_read(256*8*port + addr*8 + 4, value[63:32]); 163 endtask : read_user_reg 164 165 166 // Set the interpolation rate 167 task automatic set_interp_rate(int port, int interp_rate); 168 begin 169 logic [7:0] cic_rate = 8'd0; 170 logic [7:0] hb_enables = 2'b0; 171 172 int _interp_rate = interp_rate; 173 174 // Calculate which half bands to enable and whatever is left over set the CIC 175 while ((_interp_rate[0] == 0) && (hb_enables < NUM_HB)) begin 176 hb_enables += 1'b1; 177 _interp_rate = _interp_rate >> 1; 178 end 179 180 // CIC rate cannot be set to 0 181 cic_rate = (_interp_rate[7:0] == 8'd0) ? 8'd1 : _interp_rate[7:0]; 182 `ASSERT_ERROR(hb_enables <= NUM_HB, "Enabled halfbands may not exceed total number of half bands."); 183 `ASSERT_ERROR(cic_rate > 0 && cic_rate <= CIC_MAX_INTERP, 184 "CIC Interpolation rate must be positive, not exceed the max cic interpolation rate, and cannot equal 0!"); 185 186 // Setup DUC 187 $display("Set interpolation to %0d", interp_rate); 188 $display("- Number of enabled HBs: %0d", hb_enables); 189 $display("- CIC Rate: %0d", cic_rate); 190 write_reg(port, SR_M_ADDR, interp_rate); // Set interpolation rate in AXI rate change 191 write_reg(port, SR_INTERP_ADDR, {hb_enables, cic_rate}); // Enable HBs, set CIC rate 192 end 193 endtask 194 195 196 // Test sending packets of ones 197 task automatic send_ones(int port, int interp_rate, bit has_time); 198 begin 199 const bit [63:0] start_time = 64'h0123456789ABCDEF; 200 201 set_interp_rate(port, interp_rate); 202 203 // Setup DUC 204 write_reg(port, SR_CONFIG_ADDR, 32'd1); // Enable clear EOB 205 write_reg(port, SR_FREQ_ADDR, 32'd0); // CORDIC phase increment 206 write_reg(port, SR_SCALE_IQ_ADDR, (1 << 14)); // Scaling, set to 1 207 208 fork 209 begin 210 chdr_word_t send_payload[$]; 211 packet_info_t pkt_info; 212 213 $display("Send ones"); 214 215 // Generate a payload of all ones 216 send_payload = {}; 217 for (int i = 0; i < PKT_SIZE_BYTES/8; i++) begin 218 send_payload.push_back({16'hffff, 16'hffff, 16'hffff, 16'hffff}); 219 end 220 221 // Send two packets with EOB on the second packet 222 pkt_info = 0; 223 pkt_info.has_time = has_time; 224 pkt_info.timestamp = start_time; 225 blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info); 226 pkt_info.timestamp = start_time + SPP; 227 pkt_info.eob = 1; 228 blk_ctrl.send_packets(port, send_payload, /*data_bytes*/, /*metadata*/, pkt_info); 229 230 $display("Send ones complete"); 231 end 232 begin 233 string s; 234 chdr_word_t samples; 235 int data_bytes; 236 chdr_word_t recv_payload[$]; 237 chdr_word_t metadata[$]; 238 packet_info_t pkt_info; 239 240 $display("Check incoming samples"); 241 for (int i = 0; i < 2*interp_rate; i++) begin 242 blk_ctrl.recv_adv(port, recv_payload, data_bytes, metadata, pkt_info); 243 244 // Check the packet size 245 $sformat(s, "incorrect (drop) packet size! expected: %0d, actual: %0d", PKT_SIZE_BYTES/8, recv_payload.size()); 246 `ASSERT_ERROR(recv_payload.size() == PKT_SIZE_BYTES/8, s); 247 248 // Check the timestamp 249 if (has_time) begin 250 bit [63:0] expected_time; 251 // Calculate what the timestamp should be 252 expected_time = start_time + i * SPP; 253 $sformat(s, "Incorrect timestamp: has_time = %0d, timestamp = 0x%0X, expected 0x%0X", 254 pkt_info.has_time, pkt_info.timestamp, expected_time); 255 `ASSERT_ERROR(pkt_info.has_time == 1 && pkt_info.timestamp == expected_time, s); 256 end else begin 257 `ASSERT_ERROR(pkt_info.has_time == 0, "Packet has timestamp when it shouldn't"); 258 end 259 260 // Check EOB 261 if (i == 2*interp_rate-1) begin 262 `ASSERT_ERROR(pkt_info.eob == 1, "EOB not set on last packet"); 263 end else begin 264 `ASSERT_ERROR(pkt_info.eob == 0, 265 $sformatf("EOB unexpectedly set on packet %0d", i)); 266 end 267 268 // Check the sample values 269 samples = 64'd0; 270 for (int j = 0; j < PKT_SIZE_BYTES/8; j++) begin 271 samples = recv_payload[j]; 272 $sformat(s, "Ramp word %0d invalid! Expected a real value, Received: %0d", 2*j, samples); 273 `ASSERT_ERROR(samples >= 0, s); 274 end 275 end 276 $display("Check complete"); 277 end 278 join 279 end 280 endtask 281 282 283 //--------------------------------------------------------------------------- 284 // Test Process 285 //--------------------------------------------------------------------------- 286 287 initial begin : tb_main 288 const int port = 0; 289 test.start_tb("rfnoc_block_duc_tb"); 290 291 // Start the BFMs running 292 blk_ctrl.run(); 293 294 295 //------------------------------------------------------------------------- 296 // Reset 297 //------------------------------------------------------------------------- 298 299 test.start_test("Wait for Reset", 10us); 300 fork 301 blk_ctrl.reset_chdr(); 302 blk_ctrl.reset_ctrl(); 303 join; 304 test.end_test(); 305 306 307 //------------------------------------------------------------------------- 308 // Check NoC ID and Block Info 309 //------------------------------------------------------------------------- 310 311 test.start_test("Verify Block Info", 2us); 312 `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect NOC_ID value"); 313 `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS, "Incorrect NUM_DATA_I value"); 314 `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS, "Incorrect NUM_DATA_O value"); 315 `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU value"); 316 test.end_test(); 317 318 319 //------------------------------------------------------------------------- 320 // Test read-back regs 321 //------------------------------------------------------------------------- 322 323 begin 324 logic [63:0] val64; 325 test.start_test("Test registers", 10us); 326 read_user_reg(port, RB_NUM_HB, val64); 327 `ASSERT_ERROR(val64 == NUM_HB, "Register NUM_HB didn't read back expected value"); 328 read_user_reg(port, RB_CIC_MAX_INTERP, val64); 329 `ASSERT_ERROR(val64 ==CIC_MAX_INTERP, "Register RB_CIC_MAX_INTERP didn't read back expected value"); 330 test.end_test(); 331 end 332 333 334 //------------------------------------------------------------------------- 335 // Test various interpolation rates (no timestamp) 336 //------------------------------------------------------------------------- 337 338 begin 339 test.start_test("Test interpolation rates (with timestamp)", 0.5ms); 340 341 $display("Note: This test will take a long time!"); 342 send_ones(port, 1, 1); // HBs enabled: 0, CIC rate: 1 343 send_ones(port, 2, 1); // HBs enabled: 1, CIC rate: 1 344 send_ones(port, 3, 1); // HBs enabled: 0, CIC rate: 3 345 send_ones(port, 4, 1); // HBs enabled: 2, CIC rate: 1 346 send_ones(port, 6, 1); // HBs enabled: 1, CIC rate: 3 347 send_ones(port, 8, 1); // HBs enabled: 2, CIC rate: 2 348 send_ones(port, 12, 1); // HBs enabled: 2, CIC rate: 3 349 send_ones(port, 13, 1); // HBs enabled: 0, CIC rate: 13 350 send_ones(port, 16, 1); // HBs enabled: 2, CIC rate: 3 351 send_ones(port, 40, 1); // HBs enabled: 2, CIC rate: 20 352 353 test.end_test(); 354 end 355 356 357 //------------------------------------------------------------------------- 358 // Test various interpolation rates (without timestamp) 359 //------------------------------------------------------------------------- 360 361 begin 362 test.start_test("Test interpolation rates (no timestamp)", 0.5ms); 363 364 send_ones(port, 1, 0); // HBs enabled: 0, CIC rate: 1 365 send_ones(port, 3, 0); // HBs enabled: 0, CIC rate: 3 366 367 test.end_test(); 368 end 369 370 371 //------------------------------------------------------------------------- 372 // Test timed tune 373 //------------------------------------------------------------------------- 374 375 // This test has not been implemented because the RFNoC FFT has not been 376 // ported yet. 377 378 379 //------------------------------------------------------------------------- 380 // Finish 381 //------------------------------------------------------------------------- 382 383 // End the TB, but don't $finish, since we don't want to kill other 384 // instances of this testbench that may be running. 385 test.end_tb(0); 386 387 // Kill the clocks to end this instance of the testbench 388 rfnoc_chdr_clk_gen.kill(); 389 rfnoc_ctrl_clk_gen.kill(); 390 duc_clk_gen.kill(); 391 end 392endmodule 393