aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x300/gen_ddrlvds.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/x300/gen_ddrlvds.v')
-rw-r--r--fpga/usrp3/top/x300/gen_ddrlvds.v106
1 files changed, 106 insertions, 0 deletions
diff --git a/fpga/usrp3/top/x300/gen_ddrlvds.v b/fpga/usrp3/top/x300/gen_ddrlvds.v
new file mode 100644
index 000000000..5773b296c
--- /dev/null
+++ b/fpga/usrp3/top/x300/gen_ddrlvds.v
@@ -0,0 +1,106 @@
+
+
+module gen_ddrlvds
+ (
+ // 1X Radio Clock
+ input tx_clk_1x,
+ // 2X Radio clock
+ input tx_clk_2x,
+ // Clk to drive DCI ODDR. This is a phase shifted version of
+ // tx_clk_2x. The phase shift is to center the DCI edge in the
+ // valid window of the data in the DAC.
+ input tx_dci_clk,
+ // Reset signal synchronous to radio clock
+ input reset,
+ // Source synchronous differential clocks to DAC
+ output tx_clk_2x_p,
+ output tx_clk_2x_n,
+ // Differential frame sync to DAC
+ output tx_frame_p,
+ output tx_frame_n,
+ // Differential byte wide data to DAC.
+ // Alternates I[15:8],I[7:0],Q[15:8],Q[7:0]
+ output [7:0] tx_d_p,
+ output [7:0] tx_d_n,
+ // Input data
+ input [15:0] i,
+ input [15:0] q,
+ // Rising edge sampled on sync_dacs triggers frame sync sequence
+ input sync_dacs
+ );
+
+ reg [15:0] i_reg, q_reg;
+ reg [15:0] i_2x, q_2x;
+ reg rising_edge;
+ wire [15:0] i_and_q_2x;
+ reg sync_2x;
+
+ genvar z;
+ wire [7:0] tx_int;
+ wire tx_clk_2x_int;
+ wire tx_frame_int;
+
+ // Keep constraint to ensure these signals are not resource shared which can cause timing failures
+ (* keep = "true" *) reg phase, phase_2x, sync_dacs_reg;
+
+ wire phase_eq_phase2x = (phase == phase_2x);
+
+ always @(posedge tx_clk_1x)
+ if (reset)
+ phase <= 1'b0;
+ else
+ phase <= ~phase;
+
+
+ //
+ // Pipeline input data so that 1x to 2x clock domain jump includes no logic external to this module.
+ //
+ always @(posedge tx_clk_1x)
+ begin
+ i_reg <= i;
+ q_reg <= q;
+ sync_dacs_reg <= sync_dacs;
+ end
+
+ always @(posedge tx_clk_2x)
+ begin
+ // Move 1x data to 2x domain, mostly just to add pipeline regs
+ // for timing closure.
+ i_2x <= i_reg;
+ q_2x <= q_reg;
+ // Sample phase to determine when 1x clock edges occur.
+ // To sync multiple AD9146 DAC's an extended assertion of FRAME is required,
+ // when sync flag set, squash one rising_edge assertion which causes a 3 word assertion of FRAME,
+ // also reset sync flag. "sync_dacs" comes from 1x clk and pulse lasts 2 2x clock cycles...this is accounted for.
+ sync_2x <= (phase_eq_phase2x && sync_2x) ? 1'b0 /*RESET */ : (sync_dacs_reg) ? 1'b1 /* SET */ : sync_2x /* HOLD */;
+ rising_edge <= (phase_eq_phase2x && ~sync_2x);
+ phase_2x <= phase;
+ end
+
+ // Interleave I and Q as SDR signals
+ assign i_and_q_2x = rising_edge ? q_2x : i_2x;
+
+ generate
+ for(z = 0; z < 8; z = z + 1)
+ begin : gen_pins
+ OBUFDS obufds (.I(tx_int[z]), .O(tx_d_p[z]), .OB(tx_d_n[z]));
+ ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr
+ (.Q(tx_int[z]), .C(tx_clk_2x),
+ .CE(1'b1), .D1(i_and_q_2x[z+8]), .D2(i_and_q_2x[z]), .S(1'b0), .R(1'b0));
+ end
+ endgenerate
+
+ // Generate framing signal to identify I and Q
+ OBUFDS obufds_frame (.I(tx_frame_int), .O(tx_frame_p), .OB(tx_frame_n));
+ ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_frame
+ (.Q(tx_frame_int), .C(tx_clk_2x),
+ .CE(1'b1), .D1(~rising_edge), .D2(~rising_edge), .S(1'b0), .R(1'b0));
+
+ // Source synchronous clk
+ OBUFDS obufds_clk (.I(tx_clk_2x_int), .O(tx_clk_2x_p), .OB(tx_clk_2x_n));
+ ODDR #(.DDR_CLK_EDGE("SAME_EDGE")) oddr_clk
+ (.Q(tx_clk_2x_int), .C(tx_dci_clk),
+ .CE(1'b1), .D1(1'b1), .D2(1'b0), .S(1'b0), .R(1'b0));
+
+endmodule // gen_ddrlvds
+