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