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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
--
-- Copyright 2021 Ettus Research, a National Instruments Brand
--
-- SPDX-License-Identifier: LGPL-3.0-or-later
--
-- Module: dac_gearbox_12x8
--
-- Description:
--
-- Gearbox to expand the data width from 12 SPC to 8 SPC.
-- Input Clocks, all aligned to one another and coming from same MMCM.
-- PLL reference clock = 61.44 or 62.5 MHz.
-- RfClk: 184.32 or 187.5 MHz (3x PLL reference clock)
-- Clk1x: 122.88 or 125 MHz (2x PLL reference clock)
-- Clk2x: 245.76 or 250 MHz (4x PLL reference clock)
--
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity dac_gearbox_12x8 is
port(
Clk1x : in std_logic;
RfClk : in std_logic;
ac1Reset_n : in std_logic;
arReset_n : in std_logic;
-- Data packing: [Q11,I11,Q10,I10,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
c1DataIn : in std_logic_vector(383 downto 0);
c1DataValidIn : in std_logic;
-- Data packing: [Q7,I7,Q6,I6,...,Q3,I3,Q2,I2,Q1,I1,Q0,I0] (I in LSBs)
rDataOut : out std_logic_vector(255 downto 0) := (others => '0');
rReadyForOutput : in std_logic;
rDataValidOut : out std_logic
);
end dac_gearbox_12x8;
architecture RTL of dac_gearbox_12x8 is
constant kDataWidth : natural := 16;
constant kDataI0Lsb : natural := 0;
constant kDataI0Msb : natural := kDataWidth-1;
constant kDataQ0Lsb : natural := kDataI0Msb+1;
constant kDataQ0Msb : natural := kDataQ0Lsb+kDataWidth-1;
constant kDataI1Lsb : natural := kDataQ0Msb+1;
constant kDataI1Msb : natural := kDataI1Lsb+kDataWidth-1;
constant kDataQ1Lsb : natural := kDataI1Msb+1;
constant kDataQ1Msb : natural := kDataQ1Lsb+kDataWidth-1;
constant kDataI2Lsb : natural := kDataQ1Msb+1;
constant kDataI2Msb : natural := kDataI2Lsb+kDataWidth-1;
constant kDataQ2Lsb : natural := kDataI2Msb+1;
constant kDataQ2Msb : natural := kDataQ2Lsb+kDataWidth-1;
constant kDataI3Lsb : natural := kDataQ2Msb+1;
constant kDataI3Msb : natural := kDataI3Lsb+kDataWidth-1;
constant kDataQ3Lsb : natural := kDataI3Msb+1;
constant kDataQ3Msb : natural := kDataQ3Lsb+kDataWidth-1;
constant kDataI4Lsb : natural := kDataQ3Msb+1;
constant kDataI4Msb : natural := kDataI4Lsb+kDataWidth-1;
constant kDataQ4Lsb : natural := kDataI4Msb+1;
constant kDataQ4Msb : natural := kDataQ4Lsb+kDataWidth-1;
constant kDataI5Lsb : natural := kDataQ4Msb+1;
constant kDataI5Msb : natural := kDataI5Lsb+kDataWidth-1;
constant kDataQ5Lsb : natural := kDataI5Msb+1;
constant kDataQ5Msb : natural := kDataQ5Lsb+kDataWidth-1;
constant kDataI6Lsb : natural := kDataQ5Msb+1;
constant kDataI6Msb : natural := kDataI6Lsb+kDataWidth-1;
constant kDataQ6Lsb : natural := kDataI6Msb+1;
constant kDataQ6Msb : natural := kDataQ6Lsb+kDataWidth-1;
constant kDataI7Lsb : natural := kDataQ6Msb+1;
constant kDataI7Msb : natural := kDataI7Lsb+kDataWidth-1;
constant kDataQ7Lsb : natural := kDataI7Msb+1;
constant kDataQ7Msb : natural := kDataQ7Lsb+kDataWidth-1;
subtype Word_t is std_logic_vector(383 downto 0);
type Words_t is array(natural range<>) of Word_t;
signal rDataInDly : Words_t(3 downto 0);
signal rDataValidDly : std_logic_vector(3 downto 0) := (others => '0');
signal c1PhaseCount, c1DataValidInDly : std_logic := '0';
signal rPhaseShiftReg : std_logic_vector(2 downto 0);
begin
-----------------------------------------------------------------------------
-- Data Packing 12 SPC to 8 SPC
-----------------------------------------------------------------------------
Clk1xDataCount: process(ac1Reset_n, Clk1x)
begin
if ac1Reset_n = '0' then
c1PhaseCount <= '0';
c1DataValidInDly <= '0';
elsif rising_edge(Clk1x) then
c1DataValidInDly <= c1DataValidIn;
c1PhaseCount <= (not c1PhaseCount) and (c1DataValidIn or c1DataValidInDly);
end if;
end process;
DataClkCrossing: process(RfClk)
begin
if rising_edge(RfClk) then
rDataInDly <= rDataInDly(rDataInDly'high-1 downto 0) & c1DataIn;
end if;
end process;
-- Store clock phase information in a shift register. The shift register
-- is a 3 bit register and it used in output data packer.
PhaseClkCrossing: process(arReset_n,RfClk)
begin
if arReset_n = '0' then
rPhaseShiftReg <= (others => '0');
elsif rising_edge(RfClk) then
rPhaseShiftReg(2 downto 1) <= rPhaseShiftReg(1 downto 0);
rPhaseShiftReg(0) <= c1PhaseCount;
end if;
end process;
-----------------------------------------------------------------------------
--
-- Timing diagram: Data valid is asserted when both clock are edge aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ��\____/�����\_____/�����\_____/�����\_____/�����\_____/�����\___
-- |
-- v <- O/p data valid assertion
-- RfClk ���\___/���\___/���\___/���\___/���\___/���\___/���\___/���\___/�
-- | | |
-- c1DataValid _/���������������������|�������|�������|����������������������
-- | | |
-- c1DValidDly _________/�������������|�������|�������|������������������������������
-- | | |
-- c1PhaseCount _______/������������\|_______|__/����|�������\__________/��
-- | | |
-- v <- rPhaseSR= "001"
-- rPhaseSR(0) ________________/��������\_____|_______|_/�������\_________________
-- | |
-- v <- rPhaseSR= "010"
-- rPhaseSR(1) _________________________/��������\____|__________/�������\____________
-- |
-- v <- rPhaseSR= "100"
-- rPhaseSR(2) __________________________________/��������\_______________/�������\___
--
-- rDValidDly0 _________________/����������������������������������������������������
--
-- rDValidDly1 _________________________/��������������������������������������������
--
-- rDValidDly2 __________________________________/�����������������������������������
--
-- In this design use a single bit counter on the input clock (Clk1x) domain
-- and pass it to the RfClk domain. When data valid is asserted when both
-- clocks are rising edge aligned, only one bit in rPhaseSR high, the
-- remaining bits are zero. We use the position of the bit counter in the
-- shift register to do data packing.
--
--
-- Timing diagram: When data valid is asserted when both clock are NOT edge
-- aligned.
--
-- | | |
-- v <-Clocks edge aligned v v
-- Clk1x ��\____/�����\_____/�����\_____/�����\_____/�����\_____/�����\___
-- |
-- v <- O/p data valid assertion
-- RfClk ���\___/���\___/���\___/���\___/���\___/���\___/���\___/���\___/�
-- | | | |
-- c1DataValid ________/����������������������|�������|�������|�������|��������������
-- | | | |
-- c1DValidDly ___________________/����������|�������|�������|�������|���������������������
-- | | | |
-- c1PhaseCount ___________________/����������|�\_____|_____/�|�������|��\__________/��
-- | | | |
-- v <- rPhaseSR= "001" |
-- rPhaseSR(0) ________________/��������������|�\_____|_/�����|����������
-- | | |
-- v <- rPhaseSR= "011"
-- rPhaseSR(1) ________________/��������������|�\_____|_/����������������
-- | |
-- v <- rPhaseSR= "110"
-- rPhaseSR(2) ________________/����������������\_______/����������
-- ^
-- | <- rPhaseSR= "101"
--
-- rDValidDly0 _________________/���������������������������������������������������
--
-- rDValidDly1 _________________________/������������������������������������������
--
-- The above timing diagram is when input data valid is asserted when both
-- clocks rising edges are not aligned. In this case the more than one bit in
-- rPhaseSR is asserted which is unique to this case. As mentioned in the
-- above case, we use rPhaseSR value to determine data packing.
-- Output Data Packer
DataOut: process(RfClk)
begin
if rising_edge(RfClk) then
-- rPhaseShiftReg = "011"
rDataOut <= rDataInDly(2)(kDataQ7Msb downto kDataI0Lsb);
if rPhaseShiftReg = "110" or rPhaseShiftReg = "100" then
rDataOut <= rDataInDly(2)(kDataQ3Msb downto kDataI0Lsb) &
rDataInDly(3)(c1DataIn'length-1 downto kDataQ7Msb+1);
elsif rPhaseShiftReg = "101" or rPhaseShiftReg = "001" then
rDataOut <= rDataInDly(3)(c1DataIn'length-1 downto kDataI4Lsb);
elsif rPhaseShiftReg = "010" then
rDataOut <= rDataInDly(3)(kDataQ7Msb downto kDataI0Lsb);
end if;
end if;
end process;
DataValidOut: process(RfClk, arReset_n)
begin
if arReset_n = '0' then
rDataValidDly <= (others => '0');
rDataValidOut <= '0';
elsif rising_edge(RfClk) then
rDataValidDly <= rDataValidDly(rDataValidDly'left-1 downto 0) &
c1DataValidIn;
-- Data valid out asserting based on phase alignment RfClk and Clk1x.
-- When RfClk and Clk1x are not phase aligned.
rDataValidOut <= rDataValidDly(2) and rReadyForOutput;
-- When RfClk and Clk1x are phase aligned.
if (rPhaseShiftReg(2) xor rPhaseShiftReg(1) xor rPhaseShiftReg(0)) = '1' then
rDataValidOut <= rDataValidDly(2) and rDataValidDly(3) and rReadyForOutput;
end if;
end if;
end process;
end RTL;
|