1//
2// Copyright 2020 Ettus Research, A National Instruments Brand
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: rfnoc_block_null_src_sink_tb
7//
8
9`default_nettype none
10
11
12module rfnoc_block_null_src_sink_tb #(
13  parameter TEST_NAME  = "rfnoc_block_null_src_sink_tb",
14  parameter CHDR_W     = 64
15)(
16  /* no IO */
17);
18
19  // Include macros and time declarations for use with PkgTestExec
20  `define TEST_EXEC_OBJ test
21  `include "test_exec.svh"
22
23  import PkgTestExec::*;
24  import PkgChdrUtils::*;
25  import PkgRfnocBlockCtrlBfm::*;
26  import PkgRfnocItemUtils::*;
27
28  // Parameters
29  localparam        NOC_ID      = 32'h0000_0001;
30  localparam [9:0]  THIS_PORTID = 10'h17;
31  localparam [15:0] THIS_EPID   = 16'hDEAD;
32  localparam int    ITEM_W      = 32;
33  localparam int    NIPC        = CHDR_W/ITEM_W; // Expected Data generation only works for full words
34  localparam int    SPP         = 201;
35  localparam int    LPP         = SPP % NIPC == 0 ? SPP/NIPC : SPP/NIPC+1;
36  localparam int    NUM_PKTS    = 50;
37
38  localparam int    PORT_SRCSNK = 0;
39  localparam int    PORT_LOOP   = 1;
40
41  // Clock and Reset Definition
42  bit rfnoc_chdr_clk;
43  sim_clock_gen #(2.5) rfnoc_chdr_clk_gen (rfnoc_chdr_clk); // 400 MHz
44
45  // ----------------------------------------
46  // Instantiate DUT
47  // ----------------------------------------
48
49  // Connections to DUT as interfaces:
50  RfnocBackendIf        backend (rfnoc_chdr_clk, rfnoc_chdr_clk); // Required backend iface
51  AxiStreamIf #(32)     m_ctrl  (rfnoc_chdr_clk);                 // Required control iface
52  AxiStreamIf #(32)     s_ctrl  (rfnoc_chdr_clk);                 // Required control iface
53  AxiStreamIf #(CHDR_W) m0_chdr (rfnoc_chdr_clk);                 // Optional data iface
54  AxiStreamIf #(CHDR_W) m1_chdr (rfnoc_chdr_clk);                 // Optional data iface
55  AxiStreamIf #(CHDR_W) s0_chdr (rfnoc_chdr_clk);                 // Optional data iface
56  AxiStreamIf #(CHDR_W) s1_chdr (rfnoc_chdr_clk);                 // Optional data iface
57
58  TestExec test = new();
59
60  typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
61
62  // Bus functional model for a software block controller
63  RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl;
64
65  // DUT
66  rfnoc_block_null_src_sink #(
67    .THIS_PORTID        (THIS_PORTID),
68    .CHDR_W             (CHDR_W),
69    .ITEM_W             (ITEM_W),
70    .NIPC               (NIPC),
71    .MTU                (10)
72  ) dut (
73    .rfnoc_chdr_clk     (backend.chdr_clk),
74    .rfnoc_ctrl_clk     (backend.ctrl_clk),
75    .rfnoc_core_config  (backend.cfg),
76    .rfnoc_core_status  (backend.sts),
77    .s_rfnoc_chdr_tdata ({m1_chdr.tdata  , m0_chdr.tdata  }),
78    .s_rfnoc_chdr_tlast ({m1_chdr.tlast  , m0_chdr.tlast  }),
79    .s_rfnoc_chdr_tvalid({m1_chdr.tvalid , m0_chdr.tvalid }),
80    .s_rfnoc_chdr_tready({m1_chdr.tready , m0_chdr.tready }),
81    .m_rfnoc_chdr_tdata ({s1_chdr.tdata , s0_chdr.tdata }),
82    .m_rfnoc_chdr_tlast ({s1_chdr.tlast , s0_chdr.tlast }),
83    .m_rfnoc_chdr_tvalid({s1_chdr.tvalid, s0_chdr.tvalid}),
84    .m_rfnoc_chdr_tready({s1_chdr.tready, s0_chdr.tready}),
85    .s_rfnoc_ctrl_tdata (m_ctrl.tdata  ),
86    .s_rfnoc_ctrl_tlast (m_ctrl.tlast  ),
87    .s_rfnoc_ctrl_tvalid(m_ctrl.tvalid ),
88    .s_rfnoc_ctrl_tready(m_ctrl.tready ),
89    .m_rfnoc_ctrl_tdata (s_ctrl.tdata ),
90    .m_rfnoc_ctrl_tlast (s_ctrl.tlast ),
91    .m_rfnoc_ctrl_tvalid(s_ctrl.tvalid),
92    .m_rfnoc_ctrl_tready(s_ctrl.tready)
93  );
94
95  // ----------------------------------------
96  // Test Process
97  // ----------------------------------------
98
99  initial begin
100    // Shared Variables
101    // ----------------------------------------
102    timeout_t    timeout;
103    ctrl_word_t  rvalue;
104
105    // Initialize
106    // ----------------------------------------
107    test.start_tb({TEST_NAME,"rfnoc_block_null_src_sink_tb"});
108
109    // Start the stream endpoint BFM
110    blk_ctrl = new(backend, m_ctrl, s_ctrl);
111    blk_ctrl.add_master_data_port(m0_chdr);
112    blk_ctrl.add_slave_data_port(s0_chdr);
113    blk_ctrl.add_master_data_port(m1_chdr);
114    blk_ctrl.add_slave_data_port(s1_chdr);
115    blk_ctrl.run();
116
117    // Startup block (Software initialization)
118    // ----------------------------------------
119    test.start_test({TEST_NAME,"Flush block then reset it"});
120    begin
121      test.start_timeout(timeout, 10us, "Waiting for flush_and_reset");
122      #100;  //Wait for GSR to deassert
123      blk_ctrl.flush_and_reset();
124      test.end_timeout(timeout);
125    end
126    test.end_test();
127
128    // Run Tests
129    // ----------------------------------------
130    test.start_test({TEST_NAME,"Read Block Info"});
131    begin
132      test.start_timeout(timeout, 1us, "Waiting for block info response");
133      // Get static block info and validate it
134      `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect noc_id Value");
135      `ASSERT_ERROR(blk_ctrl.get_num_data_i() == 2, "Incorrect num_data_i Value");
136      `ASSERT_ERROR(blk_ctrl.get_num_data_o() == 2, "Incorrect num_data_o Value");
137      `ASSERT_ERROR(blk_ctrl.get_ctrl_fifosize() == 5, "Incorrect ctrl_fifosize Value");
138      `ASSERT_ERROR(blk_ctrl.get_mtu() == 10, "Incorrect mtu Value");
139
140      // Read status register and validate it
141      blk_ctrl.reg_read(dut.REG_CTRL_STATUS, rvalue);
142      `ASSERT_ERROR(rvalue[31:24] == NIPC, "Incorrect NIPC Value");
143      `ASSERT_ERROR(rvalue[23:16] == ITEM_W, "Incorrect ITEM_W Value");
144      test.end_timeout(timeout);
145    end
146    test.end_test();
147
148    test.start_test({TEST_NAME,"Stream Data Through Loopback Port m1->s1"});
149    begin
150      // Send and receive packets
151      repeat (NUM_PKTS) begin
152        chdr_word_t rx_data[$];
153        int rx_bytes;
154        automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) tx_dbuff = new;
155        automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) rx_dbuff = new;
156        for (int i = 0; i < SPP; i++)
157          tx_dbuff.put($urandom());
158        test.start_timeout(timeout, 5us, "Waiting for pkt to loop back");
159        blk_ctrl.send(PORT_LOOP, tx_dbuff.to_chdr_payload(), tx_dbuff.get_bytes());
160        blk_ctrl.recv(PORT_LOOP, rx_data, rx_bytes);
161        rx_dbuff.from_chdr_payload(rx_data, rx_bytes);
162        `ASSERT_ERROR(rx_dbuff.equal(tx_dbuff), "Data mismatch");
163        test.end_timeout(timeout);
164      end
165
166      // Read item and packet counts on loopback port
167      blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
168      `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_LOOP_LINE_CNT_LO value");
169      blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
170      `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_LOOP_PKT_CNT_LO value");
171
172      // Read item and packet counts on source port
173      blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
174      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
175      blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
176      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
177
178      // Read item and packet counts on sink port
179      blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
180      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_LINE_CNT_LO value");
181      blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
182      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_PKT_CNT_LO value");
183    end
184    test.end_test();
185
186    test.start_test({TEST_NAME,"Stream Data To Sink Port m0"});
187    begin
188      // Send packets
189      repeat (NUM_PKTS) begin
190        chdr_word_t rx_data[$];
191        int rx_bytes;
192        automatic ItemDataBuff #(logic[ITEM_W-1:0],CHDR_W) tx_dbuff = new;
193        for (int i = 0; i < SPP; i++)
194          tx_dbuff.put($urandom());
195        test.start_timeout(timeout, 5us, "Waiting to send packet");
196        blk_ctrl.send(PORT_SRCSNK, tx_dbuff.to_chdr_payload(), tx_dbuff.get_bytes());
197        test.end_timeout(timeout);
198      end
199      repeat (NUM_PKTS * SPP * 2) @(posedge rfnoc_chdr_clk);
200
201      // Read item and packet counts on loopback port
202      blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
203      `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_LOOP_LINE_CNT_LO value");
204      blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
205      `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_LOOP_PKT_CNT_LO value");
206
207      // Read item and packet counts on source port
208      blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
209      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
210      blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
211      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
212
213      // Read item and packet counts on sink port
214      blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
215      `ASSERT_ERROR(rvalue == (LPP*NUM_PKTS), "Incorrect REG_SNK_LINE_CNT_LO value");
216      blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
217      `ASSERT_ERROR(rvalue == NUM_PKTS, "Incorrect REG_SNK_PKT_CNT_LO value");
218    end
219    test.end_test();
220
221    test.start_test({TEST_NAME,"Stream Data From Source Port s0"});
222    begin
223      // Turn on the source for some time then stop it
224      blk_ctrl.reg_write(dut.REG_SRC_LINES_PER_PKT, LPP-1);
225      // A line is generated as NIPC Items
226      blk_ctrl.reg_write(dut.REG_SRC_BYTES_PER_PKT, (LPP+1)*ITEM_W/8*NIPC);
227      blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b10);
228      repeat ((NUM_PKTS / 10) * LPP) @(posedge rfnoc_chdr_clk);
229      blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b00);
230      blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
231      repeat (rvalue * LPP * 2) @(posedge rfnoc_chdr_clk);
232      blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
233
234      // Gather the accumulated packets and verify contents
235      for (int p = 0; p < rvalue; p++) begin
236        chdr_word_t exp_data[$];
237        chdr_word_t rx_data[$];
238        int rx_bytes;
239        test.start_timeout(timeout, 5us, "Waiting for pkt to arrive");
240        exp_data.delete();
241        for (int i = p*LPP; i < (p+1)*LPP; i++)
242          exp_data.push_back({NIPC{{~i[ITEM_W/2-1:0], i[ITEM_W/2-1:0]}}});
243        blk_ctrl.recv(PORT_SRCSNK, rx_data, rx_bytes);
244        `ASSERT_ERROR(blk_ctrl.compare_data(exp_data, rx_data), "Data mismatch");
245        test.end_timeout(timeout);
246      end
247    end
248    test.end_test();
249
250    test.start_test({TEST_NAME,"Clear Counts"});
251    begin
252      test.start_timeout(timeout, 1us, "Waiting for clear and readbacks");
253      // Clear
254      blk_ctrl.reg_write(dut.REG_CTRL_STATUS, 2'b01);
255
256      // Read item and packet counts on loopback port
257      blk_ctrl.reg_read(dut.REG_LOOP_LINE_CNT_LO, rvalue);
258      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_LOOP_LINE_CNT_LO value");
259      blk_ctrl.reg_read(dut.REG_LOOP_PKT_CNT_LO, rvalue);
260      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_LOOP_PKT_CNT_LO value");
261
262      // Read item and packet counts on source port
263      blk_ctrl.reg_read(dut.REG_SRC_LINE_CNT_LO, rvalue);
264      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_LINE_CNT_LO value");
265      blk_ctrl.reg_read(dut.REG_SRC_PKT_CNT_LO, rvalue);
266      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SRC_PKT_CNT_LO value");
267
268      // Read item and packet counts on sink port
269      blk_ctrl.reg_read(dut.REG_SNK_LINE_CNT_LO, rvalue);
270      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_LINE_CNT_LO value");
271      blk_ctrl.reg_read(dut.REG_SNK_PKT_CNT_LO, rvalue);
272      `ASSERT_ERROR(rvalue == 0, "Incorrect REG_SNK_PKT_CNT_LO value");
273      test.end_timeout(timeout);
274    end
275    test.end_test();
276
277    // Finish Up
278    // ----------------------------------------
279    // Display final statistics and results
280    test.end_tb(.finish(0));
281  end
282
283endmodule
284