aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v')
-rw-r--r--fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v120
1 files changed, 120 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v b/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v
new file mode 100644
index 000000000..604c04f50
--- /dev/null
+++ b/fpga/usrp3/top/x400/rf/100m/adc_gearbox_2x1.v
@@ -0,0 +1,120 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: adc_gearbox_2x1
+//
+// Description:
+//
+// Gearbox ADC data from 2 SPC to 1 SPC and corresponding 2x clock to 1x
+// clock. Also implement data swapping to format packets to fit the FIR
+// filter input requirements.
+//
+// This modules incurs one clk1x cycle of delay on the data and valid signals
+// from input on the 1x domain to output on the 2x domain.
+//
+
+`default_nettype none
+
+module adc_gearbox_2x1 (
+ input wire clk1x,
+ input wire reset_n_1x,
+ // Data is _presumed_ to be packed [Sample1, Sample0] (Sample0 in LSBs).
+ input wire [31:0] adc_q_in_1x,
+ input wire [31:0] adc_i_in_1x,
+ input wire valid_in_1x,
+ // De-assert enable_1x to clear the data synchronously from this module.
+ input wire enable_1x,
+
+ input wire clk2x,
+ // Data is packed [Q,I] (I in LSBs) when swap_iq_1x is '0'.
+ input wire swap_iq_2x,
+ output wire [31:0] adc_out_2x,
+ output wire valid_out_2x
+);
+
+ // Re-create the 1x clock in the 2x domain to produce a deterministic
+ // crossing.
+ reg toggle_1x, toggle_2x = 1'b0, toggle_2x_dly = 1'b0, valid_2x = 1'b0, valid_dly_2x = 1'b0;
+ reg [31:0] data_out_2x = 32'b0, adc_q_data_in_2x = 32'b0, adc_i_data_in_2x = 32'b0;
+
+ // Create a toggle in the 1x clock domain (clock divider /2).
+ always @(posedge clk1x or negedge reset_n_1x) begin
+ if ( ! reset_n_1x) begin
+ toggle_1x <= 1'b0;
+ end else begin
+ toggle_1x <= ! toggle_1x;
+ end
+ end
+
+ // clk1x and clk2x are nominally aligned on their rising edges, but clk2x is
+ // more heavily loaded, which results in a later arrival time. That late
+ // arrival causes large estimated hold violations after place. The Ultrafast
+ // method (UG 949) suggests fixing post-place hold violations that are worse
+ // than -0.5 ns.
+ // Resampling 1x signals on the falling edge of clk2x provides nominally half
+ // a period of setup and half a period of hold. The late arrival of clk2x
+ // shifts some of that margin away from hold slack and into setup slack.
+ reg toggle_2x_fall = 1'b0;
+ reg [31:0] adc_q_in_2x_fall = 32'b0;
+ reg [31:0] adc_i_in_2x_fall = 32'b0;
+ reg valid_in_2x_fall = 1'b0;
+ reg enable_2x_fall = 1'b0;
+
+ always @(negedge clk2x) begin
+ toggle_2x_fall <= toggle_1x;
+ adc_q_in_2x_fall <= adc_q_in_1x;
+ adc_i_in_2x_fall <= adc_i_in_1x;
+ valid_in_2x_fall <= valid_in_1x;
+ enable_2x_fall <= enable_1x;
+ end
+
+ // Transfer the toggle from the 1x to the 2x domain. Delay the toggle in the
+ // 2x domain by one cycle and compare it to the non-delayed version. When
+ // they differ, push data_in[15:0] onto the output. When the match, push
+ // [31:16] onto the output. The datasheet is unclear on the exact
+ // implementation.
+ //
+ // It is safe to not reset this domain because all of the input signals will
+ // be cleared by the 1x reset. Safe default values are assigned to all these
+ // registers.
+ always @(posedge clk2x) begin
+ toggle_2x <= toggle_2x_fall;
+ toggle_2x_dly <= toggle_2x;
+ adc_q_data_in_2x <= adc_q_in_2x_fall;
+ adc_i_data_in_2x <= adc_i_in_2x_fall;
+ // Place Q in the MSBs, I in the LSBs by default, unless swapped = 1.
+ if (valid_2x) begin
+ if (swap_iq_2x) begin
+ if (toggle_2x != toggle_2x_dly) begin
+ data_out_2x[31:16] <= adc_i_data_in_2x[15:0];
+ data_out_2x[15: 0] <= adc_q_data_in_2x[15:0];
+ end else begin
+ data_out_2x[31:16] <= adc_i_data_in_2x[31:16];
+ data_out_2x[15: 0] <= adc_q_data_in_2x[31:16];
+ end
+ end else begin
+ if (toggle_2x != toggle_2x_dly) begin
+ data_out_2x[31:16] <= adc_q_data_in_2x[15:0];
+ data_out_2x[15: 0] <= adc_i_data_in_2x[15:0];
+ end else begin
+ data_out_2x[31:16] <= adc_q_data_in_2x[31:16];
+ data_out_2x[15: 0] <= adc_i_data_in_2x[31:16];
+ end
+ end
+ end else begin
+ data_out_2x <= 32'b0;
+ end
+ // Valid is simply a transferred version of the 1x clock's valid. Delay it
+ // one more cycle to align outputs.
+ valid_2x <= valid_in_2x_fall && enable_2x_fall;
+ valid_dly_2x <= valid_2x;
+ end
+
+ assign adc_out_2x = data_out_2x;
+ assign valid_out_2x = valid_dly_2x;
+
+endmodule
+
+`default_nettype wire