aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/x300/gen_ddrlvds.v
blob: 7b0e4a6a1d614f332fe44fe90af9c1fed9568616 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
module gen_ddrlvds (
   // 2X Radio clock
   input tx_clk_2x,
   // 1X Radio Clock
   input tx_clk_1x,
   // 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
);

   localparam SYNC_PULSE_WIDTH = 3'd2;

   //
   // Figure out the 1X clock level
   //
   localparam TX_CLK_1X_LOW   = 1'b0;
   localparam TX_CLK_1X_HIGH  = 1'b1;

   reg   tx_clk_1x_level;
   reg   phase, phase_2x;
   reg   reset_2x;

   always @(posedge tx_clk_1x)
      if (reset)
         phase <= 1'b0;
      else
         phase <= ~phase;

   always @(posedge tx_clk_2x)
   begin
      phase_2x <= phase;
      //Pipeline reset and tx_clk_1x_level
      reset_2x <= reset;
      tx_clk_1x_level <= (phase == phase_2x) ? TX_CLK_1X_HIGH : TX_CLK_1X_LOW;
   end

   //
   // Pipeline input data so that 1x to 2x clock domain jump includes no logic external to this module.
   //
   reg [15:0]  i_reg, q_reg;
   reg         sync_dacs_reg;

   always @(posedge tx_clk_1x) 
   begin
      i_reg <= i;
      q_reg <= q;
      sync_dacs_reg <= sync_dacs;
   end  

   //
   // Generate frame signal and interleave I and Q signals
   //
   reg [15:0]  i_2x, q_2x;
   reg [2:0]   sync_count;
   reg         frame;

   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 frame assertion which causes a SYNC_PULSE_WIDTH+1 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.
      if (reset_2x) begin
         frame <= 0;
         sync_count <= 3'd0;
      end else begin
         frame <= (tx_clk_1x_level == TX_CLK_1X_LOW) | (sync_count != 3'd0);
         if ((tx_clk_1x_level == TX_CLK_1X_LOW) & sync_dacs_reg)
            sync_count <= SYNC_PULSE_WIDTH;
         else if (sync_count > 3'd0)
            sync_count <= sync_count - 3'd1;
      end
   end

   wire [15:0] i_and_q_2x = frame ? i_2x : q_2x;
   
   //
   // Instantiate IO primitives for the source synchronous interface
   //
   wire [7:0]  tx_int;
   wire        tx_clk_2x_int;
   wire        tx_frame_int;

   genvar z;
   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(frame), .D2(frame), .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_clk_2x),
      .CE(1'b1), .D1(1'b1), .D2(1'b0), .S(1'b0), .R(1'b0));

endmodule // gen_ddrlvds