1//
2// Copyright 2016 Ettus Research, A National Instruments Company
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: cat_io_lvds_tb
7//
8// Description: Testbench for cat_io_lvds.
9//
10
11`timescale 1ns/1ps
12
13module cat_io_lvds_tb();
14
15  localparam CLK_PERIOD    = 10;
16  localparam CLK200_PERIOD = 2.5;
17
18  localparam FRAME_SAMPLE = 0;
19
20  localparam USE_CLOCK_IDELAY   = 1;
21  localparam USE_DATA_IDELAY    = 1;
22  localparam DATA_IDELAY_MODE   = "VAR_LOAD";
23  localparam CLOCK_IDELAY_MODE  = "VAR_LOAD";
24  localparam INPUT_CLOCK_DELAY  = 0;
25  localparam INPUT_DATA_DELAY   = 0;
26  localparam USE_CLOCK_ODELAY   = 1;
27  localparam USE_DATA_ODELAY    = 1;
28  localparam DATA_ODELAY_MODE   = "VAR_LOAD";
29  localparam CLOCK_ODELAY_MODE  = "VAR_LOAD";
30  localparam OUTPUT_CLOCK_DELAY = 0;
31  localparam OUTPUT_DATA_DELAY  = 0;
32
33  reg [8*19:0] test_status;
34
35  reg       clk      = 0;
36  reg       rx_clk   = 0;
37  reg       clk200   = 0;
38
39  reg       reset;
40  reg       mimo;
41  reg [5:0] rx_d;
42  reg       rx_frame;
43  reg [7:0] count;
44
45//  initial $dumpfile("catcap_ddr_lvds_tb.vcd");
46//  initial $dumpvars(0,catcap_ddr_lvds_tb);
47
48  wire [11:0] i0 = {4'hA,count};
49  wire [11:0] q0 = {4'hB,count};
50  wire [11:0] i1 = {4'hC,count};
51  wire [11:0] q1 = {4'hD,count};
52
53  wire radio_clk;
54
55  reg [11:0] tx_i0;
56  reg [11:0] tx_q0;
57  reg [11:0] tx_i1;
58  reg [11:0] tx_q1;
59
60  wire [11:0] rx_i0;
61  wire [11:0] rx_q0;
62  wire [11:0] rx_i1;
63  wire [11:0] rx_q1;
64
65  wire rx_aligned;
66
67  wire        tx_clk_p, tx_clk_n;
68  wire        tx_frame_p, tx_frame_n;
69  wire [5:0]  tx_d_p, tx_d_n;
70
71  reg [4:0]  ctrl_in_data_delay;
72  reg [4:0]  ctrl_in_clk_delay;
73  reg        ctrl_ld_in_data_delay;
74  reg        ctrl_ld_in_clk_delay;
75
76  reg [4:0]  ctrl_out_data_delay;
77  reg [4:0]  ctrl_out_clk_delay;
78  reg        ctrl_ld_out_data_delay;
79  reg        ctrl_ld_out_clk_delay;
80
81
82  //---------------------------------------------------------------------------
83  // Clock Generation
84  //---------------------------------------------------------------------------
85
86  // IODELAYCTRL reference clock
87  always #(CLK200_PERIOD) clk200 = ~clk200;
88
89  // Create an internal clock we'll use to drive the data
90  always #(CLK_PERIOD) clk = ~clk;
91
92  // RF interface clock. Half the rate of clk and out of phase
93  always @(negedge clk) rx_clk <= ~rx_clk;
94
95
96  //---------------------------------------------------------------------------
97  // Tasks
98  //---------------------------------------------------------------------------
99
100  task BURST;
101    input [31:0] len;
102    input        do_mimo;
103    begin
104      count <= 0;
105      repeat(len)
106        begin
107          mimo <= do_mimo;
108
109          // Channel 0 sample
110          @(posedge clk);
111          rx_d <= i0[11:6];
112          rx_frame <= 1;
113          @(posedge clk);
114          rx_d <= q0[11:6];
115          rx_frame <= 1;
116          @(posedge clk);
117          rx_d <= i0[5:0];
118          rx_frame <= ~FRAME_SAMPLE;
119          @(posedge clk);
120          rx_d <= q0[5:0];
121          rx_frame <= ~FRAME_SAMPLE;
122
123          if (do_mimo) begin
124            // Channel 1 sample
125            @(posedge clk);
126            rx_d <= i1[11:6];
127            rx_frame <= FRAME_SAMPLE;
128            @(posedge clk);
129            rx_d <= q1[11:6];
130            rx_frame <= FRAME_SAMPLE;
131            @(posedge clk);
132            rx_d <= i1[5:0];
133            rx_frame <= 0;
134            @(posedge clk);
135            rx_d <= q1[5:0];
136            rx_frame <= 0;
137          end else begin
138            if (!FRAME_SAMPLE) begin
139              // When we frame every two samples (one from each channel), in
140              // MIMO mode, we should only grab channel 0. So input garbage on
141              // channel 1 to make sure the data doesn't get used.
142              @(posedge clk);
143              rx_d <= 6'bXXXXXX;
144              rx_frame <= FRAME_SAMPLE;
145              @(posedge clk);
146              rx_d <= 6'bXXXXXX;
147              rx_frame <= FRAME_SAMPLE;
148              @(posedge clk);
149              rx_d <= 6'bXXXXXX;
150              rx_frame <= 0;
151              @(posedge clk);
152              rx_d <= 6'bXXXXXX;
153              rx_frame <= 0;
154            end else begin
155              // When every sample is framed, we might sync align to either
156              // channel (no way to tell them apart), so input the channel 1
157              // data, but we should only see one channel's data duplicated on
158              // both outputs.
159              @(posedge clk);
160              rx_d <= i1[11:6];
161              rx_frame <= FRAME_SAMPLE;
162              @(posedge clk);
163              rx_d <= q1[11:6];
164              rx_frame <= FRAME_SAMPLE;
165              @(posedge clk);
166              rx_d <= i1[5:0];
167              rx_frame <= 0;
168              @(posedge clk);
169              rx_d <= q1[5:0];
170              rx_frame <= 0;
171            end
172          end
173
174          count <= count + 1;
175        end
176    end
177  endtask // BURST
178
179
180  //---------------------------------------------------------------------------
181  // Test Procedure
182  //---------------------------------------------------------------------------
183
184  initial
185    begin
186      // Initial values
187      test_status <= "Reset";
188      reset = 1;
189      mimo = 1;
190      ctrl_in_data_delay = 5'd0;
191      ctrl_in_clk_delay = 5'd8;
192      ctrl_ld_in_data_delay = 1'b0;
193      ctrl_ld_in_clk_delay = 1'b0;
194      ctrl_out_data_delay = 5'd0;
195      ctrl_out_clk_delay = 5'd16;
196      ctrl_ld_out_data_delay = 1'b0;
197      ctrl_ld_out_clk_delay = 1'b0;
198      repeat(10) @(negedge rx_clk);
199      reset = 0;
200      @(negedge rx_clk);
201
202      // Load new input delay values
203      test_status <= "Load input delays";
204      ctrl_ld_in_data_delay = 1'b1;
205      ctrl_ld_in_clk_delay = 1'b1;
206      @(negedge rx_clk);
207      ctrl_ld_in_data_delay = 1'b0;
208      ctrl_ld_in_clk_delay = 1'b0;
209
210      // Load new output delay values
211      test_status <= "Load output delays";
212      ctrl_ld_out_data_delay = 1'b1;
213      ctrl_ld_out_clk_delay = 1'b1;
214      @(negedge rx_clk);
215      ctrl_ld_out_data_delay = 1'b0;
216      ctrl_ld_out_clk_delay = 1'b0;
217
218      // Input data until the Rx circuit aligns
219      test_status <= "Wait align";
220      while (!rx_aligned) begin
221        BURST(1,1);
222      end
223
224      // Input some new samples
225      test_status <= "Burst 1 (MIMO)";
226      BURST(30, 1);
227
228      // Reset and do another burst
229      test_status <= "Reset 2";
230      reset = 1;
231      repeat(20) @(negedge rx_clk);
232      reset = 0;
233      repeat(2) @(negedge rx_clk);
234
235      // Input data until the Rx circuit aligns
236      test_status <= "Wait align 2";
237      while (!rx_aligned) begin
238        BURST(1,1);
239      end
240
241      // Input some new samples
242      test_status <= "Burst 2 (MIMO)";
243      BURST(30, 1);
244
245      // Reset and do another burst
246      test_status <= "Reset 3";
247      reset = 1;
248      repeat(20) @(negedge rx_clk);
249      reset = 0;
250      repeat(2) @(negedge rx_clk);
251
252      // Input data until the Rx circuit aligns in SISO mode
253      test_status <= "Wait align 3";
254      while (!rx_aligned) begin
255        BURST(1,0);
256      end
257
258      // Switch to SISO mode
259      test_status <= "Burst 3 (SISO)";
260      BURST(25,0);
261
262      repeat(50) @(negedge rx_clk);
263
264
265      $finish;
266    end
267
268
269  //---------------------------------------------------------------------------
270  // DUT
271  //---------------------------------------------------------------------------
272
273  // Loop the Rx interface of cat_io_lvds back to its Tx interface
274  always @(posedge radio_clk) begin
275    tx_i0 = rx_i0;
276    tx_q0 = rx_q0;
277    tx_i1 = rx_i1;
278    tx_q1 = rx_q1;
279  end
280
281  cat_io_lvds #(
282    .INVERT_FRAME_RX    (0),
283    .INVERT_DATA_RX     (6'b00_0000),
284    .INVERT_FRAME_TX    (0),
285    .INVERT_DATA_TX     (6'b00_0000),
286    .USE_CLOCK_IDELAY   (USE_CLOCK_IDELAY  ),
287    .USE_DATA_IDELAY    (USE_DATA_IDELAY   ),
288    .DATA_IDELAY_MODE   (DATA_IDELAY_MODE  ),
289    .CLOCK_IDELAY_MODE  (CLOCK_IDELAY_MODE ),
290    .INPUT_CLOCK_DELAY  (INPUT_CLOCK_DELAY ),
291    .INPUT_DATA_DELAY   (INPUT_DATA_DELAY  ),
292    .USE_CLOCK_ODELAY   (USE_CLOCK_ODELAY  ),
293    .USE_DATA_ODELAY    (USE_DATA_ODELAY   ),
294    .DATA_ODELAY_MODE   (DATA_ODELAY_MODE  ),
295    .CLOCK_ODELAY_MODE  (CLOCK_ODELAY_MODE ),
296    .OUTPUT_CLOCK_DELAY (OUTPUT_CLOCK_DELAY),
297    .OUTPUT_DATA_DELAY  (OUTPUT_DATA_DELAY )
298  ) cat_io_lvds_i0 (
299    .rst    (reset),
300    .clk200 (clk200),
301
302    // Data and frame timing
303    .mimo         (mimo),
304    .frame_sample (FRAME_SAMPLE[0]),
305
306    // Delay control interface
307    .ctrl_clk               (rx_clk),
308    //
309    .ctrl_in_data_delay     (ctrl_in_data_delay),
310    .ctrl_in_clk_delay      (ctrl_in_clk_delay),
311    .ctrl_ld_in_data_delay  (ctrl_ld_in_data_delay),
312    .ctrl_ld_in_clk_delay   (ctrl_ld_in_clk_delay),
313    //
314    .ctrl_out_data_delay    (ctrl_out_data_delay),
315    .ctrl_out_clk_delay     (ctrl_out_clk_delay),
316    .ctrl_ld_out_data_delay (ctrl_ld_out_data_delay),
317    .ctrl_ld_out_clk_delay  (ctrl_ld_out_clk_delay),
318
319    // Baseband sample interface
320    .radio_clk    (radio_clk),
321    .radio_clk_2x (),
322    .rx_aligned   (rx_aligned),
323    //
324    .rx_i0        (rx_i0),
325    .rx_q0        (rx_q0),
326    .rx_i1        (rx_i1),
327    .rx_q1        (rx_q1),
328    //
329    .tx_i0        (tx_i0),
330    .tx_q0        (tx_q0),
331    .tx_i1        (tx_i1),
332    .tx_q1        (tx_q1),
333
334    // Catalina interface
335    .rx_clk_p   (rx_clk),
336    .rx_clk_n   (~rx_clk),
337    .rx_frame_p (rx_frame),
338    .rx_frame_n (~rx_frame),
339    .rx_d_p     (rx_d),
340    .rx_d_n     (~rx_d),
341    //
342    .tx_clk_p   (tx_clk_p),
343    .tx_clk_n   (tx_clk_n),
344    .tx_frame_p (tx_frame_p),
345    .tx_frame_n (tx_frame_n),
346    .tx_d_p     (tx_d_p),
347    .tx_d_n     (tx_d_n)
348  );
349
350endmodule // cat_io_lvds_tb
351