本实验目标
• 掌握并行DAC、ADC的接口时序
• 用DDS合成信号,经过DAC输出
• 用ADC采集信号
• 使用SignalTAP在时域观察信号
• 使用Matlab在频域观察信号
关于高速数据转换器的时钟管理关于高速数据转换器的时钟管理:
• ACLK和DCLK的区别
– 用于进行数据转换的ACLK,应尽量高质量
– 最好不要经过FPGA,否则该时钟的波形质量会下降
– 用同步数据的DCLK,只要满足数据的建立保持特性即可
• 本实验配套电路板的局限性
– 由于成本问题,本实验配套电路板没有采用时钟管理芯片
– DAC、ADC的数据转换时钟经过了FPGA芯片内部
– 对于高指标的设计不能这样做
本参考设计的时钟方案:
• 使用FPGA片内的锁相环
– 由50MHz晶振的时钟信号
– 倍频得到80MHz的DAC时钟
– 分频得到20MHz的ADC时钟
• 用FPGA的逻辑对内部的80MHz时钟反相作为DAC 芯片时钟,用于满足DAC数据的建立-保持时序
• ADC芯片在时钟的上跳锁存数据
• ADC时钟同样进行反相,作为ADC接口逻辑的驱动时钟
• 注意:这种用FPGA的内部逻辑电路进行ADC、 DAC的时钟相位调整的方法,其时钟的抖动和相位噪声不好。高质量设计中,通常用专门的时钟管理芯片完成时钟的相位调节
转换器的垂直域(电压、数值)问题:
• AD9762是无符号的DAC器件
• 有符号补码需要先把高位取反再送给DAC
– 请自行推导此时的有符号补码数和无符号数之间的对应关系
• AD9200是无符号的ADC
– 最大电压对应MAX值
– 0电压对应0值
• 注意ADC芯片还有一个溢出 out of range 信号
• ADC输入的电压如果超限,则溢出信号置位1
• 数据溢出对于ADC的采样结果是灾难性的
• 尤其是对于频域处理算法
• 一旦检测到ADC溢出,必须先调整其不溢出之后,再进行其他处理动作。
一、顶层结构:
• 观察一下顶层结构
– 该设计由一个PLL从50MHz晶振生成80MHz的DAC时钟、 20MHzADC时钟
– 一个单周期的DDS生成补码正弦波,转成无符号后送至DAC
– 拨码开关SW9 SW8 用于控制DAC的输出幅度
– 其余的SW开关用于控制DDS频率字
ADC接口:
module ADC_interface(
CLK_ADC , // adc clk
DAT_ADC , // input data, from adc
OTR_ADC , // from adc , signal out of range flag
OTR_OUT , // output otr flag
STBY_ADC , // adc stand by, set 0 make adc running
DOUT ); // data out
input CLK_ADC ;
input [9:0] DAT_ADC ;
input OTR_ADC ;
output OTR_OUT ;
output STBY_ADC ;
output [9:0] DOUT ;
reg [9:0] DOUT ;
reg OTR_OUT;
always @ (posedge CLK_ADC) begin
DOUT <= DAT_ADC ;
OTR_OUT <= OTR_ADC;
end
assign STBY_ADC = 1'b0;
endmodule
DAC接口:
DAC 接口模块 转换2补码为无符号数 /
// input is signed , output is unsigned
module DAC_interface(
CLKIN , // input clk
DATIN , // input data
SCALE , // scale factor, right shift the data
DAT2DAC ); // data to dac
input CLKIN;
input [8-1:0] DATIN ;
input [2-1:0] SCALE ;
output [12-1:0] DAT2DAC ;
reg [12-1:0] DAT2DAC ;
reg [12-1:0] datin_R1;
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[7]; // inverse the msb to unsigned
datin_R1 [10: 4] <= DATIN[6:0];
datin_R1 [3 : 0] <= 0;
DAT2DAC <= (datin_R1 >> SCALE);
end
endmodule
DDS:
// mc_dds
// multi-cycle dds module
module mc_dds(
CLK , // clock posedge
RST , // reset posedge
FREQIN, // input frequency word
FREQEN, // frequency word input enable
DDSEN , // multi-cycle dds work enable
OUTVD , // dds output valid
DDSOUT); // dds output wave
input CLK ;
input RST ;
input [32-1:0] FREQIN;
input FREQEN;
input DDSEN ;
output OUTVD ;
output [7 :0] DDSOUT;
// work enable delay line
reg ddsen_R1, ddsen_R2, ddsen_R3;
reg [32-1:0] freqin_R ;
reg [32-1:0] phase_acc_R ;
reg [8-1:0] DDSOUT ;
wire [8-1:0] rom_rdW ; // rom read data
wire [7-1:0] rom_raW ; // rom read address
// code template
// always @ (posedge CLK or posedge RST) begin
// if(RST) begin
// end
// else begin
// end
// end
always @ (posedge CLK or posedge RST) begin
if(RST) begin
ddsen_R1 <= 1'b0;
ddsen_R2 <= 1'b0;
ddsen_R3 <= 1'b0;
end
else begin
ddsen_R1 <= DDSEN ;
ddsen_R2 <= ddsen_R1;
ddsen_R3 <= ddsen_R2;
end
end
assign OUTVD = ddsen_R3;
// dds working pipeline
// OUTVD
// DDSEN | ddsen_R1 |ddsen_R2 |ddsen_R3
// | phase_acc_R |
// | rom_raW | rom_rdW | DDSOUT
always @ (posedge CLK or posedge RST) begin
if(RST) begin
phase_acc_R <= 0;
freqin_R <= 0;
end
else begin
if(FREQEN) begin
freqin_R <= FREQIN;
end // if(FREQEN)
if(DDSEN) begin
phase_acc_R <= phase_acc_R + freqin_R;
end // if(DDSEN)
if(ddsen_R2) begin
DDSOUT <= rom_rdW;
end
end
end
assign rom_raW = phase_acc_R[32-1: 32-1-7+1];
sine_rom U_sine_rom(
.CLK (CLK ), // clock
.RA (rom_raW ), // read address
.RD (rom_rdW )); // read data
endmodule // mc_dds();
下载实验的工程sof文件
• 拨动控制DAC幅度和DDS频率的开关
• 在signaltap中观察ADC的输出信号
• 注意观察ADC的溢出信号OTR和ADC数据波形的对应关系
在SignalTap中观察ADC的输出信号
顶部切割失真是由于信号幅值过高产生了数据溢出,将溢出信号OTR变为0,失真现象消失。
观察ADC的数据频谱:
学生实验
• 修改代码完成以下任务
• 把DDS的rom改成16比特,高12位送给DAC
• 不要忘记缩放DAC的输出缩放
• 生成不同频率的正弦波
– 仍然保持80MHz的DAC时钟速率
– 用3比特的拨码开关设定输出正弦波频率
– 000~ 0.5MHz、001 ~ 1MHz 、010 ~ 1.5MHz、011 ~ 2MHz – 100~ 2.5MHz、101 ~ 3.5MHz、110 ~ 4.5MHz、111 ~ 5.5MHz
• 使用20MHz的ADC采样频率
• 把数据导入到Matlab
– 观察频谱纯度
– 确认你生成的信号频率正确
// ************************************************************** //
// FILE : DDS_CORE_ROM.v
// DSCP : DDS CORE ROM FILE
// ABOUT : auto generated rom file by gen_rom_rtl.m
// DATE : 22-Jul-2021 14:45:32
// ************************************************************** //
// module DDS_CORE_ROM()
module DDS_CORE_ROM(
CLK , // clock
RA , // read address
RD ); // read data
input CLK;
input [6 :0] RA;
output [15 :0] RD;
reg [15 :0] RD;
always @ (posedge CLK)
case(RA)
7 'd 0 :RD = #1 16'b 0000000000000000; // 0 0x0
7 'd 1 :RD = #1 16'b 0000011001000111; // 1607 0x647
7 'd 2 :RD = #1 16'b 0000110010001011; // 3211 0xC8B
7 'd 3 :RD = #1 16'b 0001001011000111; // 4807 0x12C7
7 'd 4 :RD = #1 16'b 0001100011111000; // 6392 0x18F8
7 'd 5 :RD = #1 16'b 0001111100011001; // 7961 0x1F19
7 'd 6 :RD = #1 16'b 0010010100100111; // 9511 0x2527
7 'd 7 :RD = #1 16'b 0010101100011110; // 11038 0x2B1E
7 'd 8 :RD = #1 16'b 0011000011111011; // 12539 0x30FB
7 'd 9 :RD = #1 16'b 0011011010111001; // 14009 0x36B9
7 'd 10 :RD = #1 16'b 0011110001010110; // 15446 0x3C56
7 'd 11 :RD = #1 16'b 0100000111001101; // 16845 0x41CD
7 'd 12 :RD = #1 16'b 0100011100011100; // 18204 0x471C
7 'd 13 :RD = #1 16'b 0100110000111111; // 19519 0x4C3F
7 'd 14 :RD = #1 16'b 0101000100110011; // 20787 0x5133
7 'd 15 :RD = #1 16'b 0101010111110100; // 22004 0x55F4
7 'd 16 :RD = #1 16'b 0101101010000001; // 23169 0x5A81
7 'd 17 :RD = #1 16'b 0101111011010110; // 24278 0x5ED6
7 'd 18 :RD = #1 16'b 0110001011110001; // 25329 0x62F1
7 'd 19 :RD = #1 16'b 0110011011001110; // 26318 0x66CE
7 'd 20 :RD = #1 16'b 0110101001101100; // 27244 0x6A6C
7 'd 21 :RD = #1 16'b 0110110111001001; // 28105 0x6DC9
7 'd 22 :RD = #1 16'b 0111000011100001; // 28897 0x70E1
7 'd 23 :RD = #1 16'b 0111001110110101; // 29621 0x73B5
7 'd 24 :RD = #1 16'b 0111011001000000; // 30272 0x7640
7 'd 25 :RD = #1 16'b 0111100010000011; // 30851 0x7883
7 'd 26 :RD = #1 16'b 0111101001111100; // 31356 0x7A7C
7 'd 27 :RD = #1 16'b 0111110000101001; // 31785 0x7C29
7 'd 28 :RD = #1 16'b 0111110110001001; // 32137 0x7D89
7 'd 29 :RD = #1 16'b 0111111010011100; // 32412 0x7E9C
7 'd 30 :RD = #1 16'b 0111111101100001; // 32609 0x7F61
7 'd 31 :RD = #1 16'b 0111111111010111; // 32727 0x7FD7
7 'd 32 :RD = #1 16'b 0111111111111111; // 32767 0x7FFF
7 'd 33 :RD = #1 16'b 0111111111010111; // 32727 0x7FD7
7 'd 34 :RD = #1 16'b 0111111101100001; // 32609 0x7F61
7 'd 35 :RD = #1 16'b 0111111010011100; // 32412 0x7E9C
7 'd 36 :RD = #1 16'b 0111110110001001; // 32137 0x7D89
7 'd 37 :RD = #1 16'b 0111110000101001; // 31785 0x7C29
7 'd 38 :RD = #1 16'b 0111101001111100; // 31356 0x7A7C
7 'd 39 :RD = #1 16'b 0111100010000011; // 30851 0x7883
7 'd 40 :RD = #1 16'b 0111011001000000; // 30272 0x7640
7 'd 41 :RD = #1 16'b 0111001110110101; // 29621 0x73B5
7 'd 42 :RD = #1 16'b 0111000011100001; // 28897 0x70E1
7 'd 43 :RD = #1 16'b 0110110111001001; // 28105 0x6DC9
7 'd 44 :RD = #1 16'b 0110101001101100; // 27244 0x6A6C
7 'd 45 :RD = #1 16'b 0110011011001110; // 26318 0x66CE
7 'd 46 :RD = #1 16'b 0110001011110001; // 25329 0x62F1
7 'd 47 :RD = #1 16'b 0101111011010110; // 24278 0x5ED6
7 'd 48 :RD = #1 16'b 0101101010000001; // 23169 0x5A81
7 'd 49 :RD = #1 16'b 0101010111110100; // 22004 0x55F4
7 'd 50 :RD = #1 16'b 0101000100110011; // 20787 0x5133
7 'd 51 :RD = #1 16'b 0100110000111111; // 19519 0x4C3F
7 'd 52 :RD = #1 16'b 0100011100011100; // 18204 0x471C
7 'd 53 :RD = #1 16'b 0100000111001101; // 16845 0x41CD
7 'd 54 :RD = #1 16'b 0011110001010110; // 15446 0x3C56
7 'd 55 :RD = #1 16'b 0011011010111001; // 14009 0x36B9
7 'd 56 :RD = #1 16'b 0011000011111011; // 12539 0x30FB
7 'd 57 :RD = #1 16'b 0010101100011110; // 11038 0x2B1E
7 'd 58 :RD = #1 16'b 0010010100100111; // 9511 0x2527
7 'd 59 :RD = #1 16'b 0001111100011001; // 7961 0x1F19
7 'd 60 :RD = #1 16'b 0001100011111000; // 6392 0x18F8
7 'd 61 :RD = #1 16'b 0001001011000111; // 4807 0x12C7
7 'd 62 :RD = #1 16'b 0000110010001011; // 3211 0xC8B
7 'd 63 :RD = #1 16'b 0000011001000111; // 1607 0x647
7 'd 64 :RD = #1 16'b 0000000000000000; // 0 0x0
7 'd 65 :RD = #1 16'b 1111100110111001; // -1607 0xF9B9
7 'd 66 :RD = #1 16'b 1111001101110101; // -3211 0xF375
7 'd 67 :RD = #1 16'b 1110110100111001; // -4807 0xED39
7 'd 68 :RD = #1 16'b 1110011100001000; // -6392 0xE708
7 'd 69 :RD = #1 16'b 1110000011100111; // -7961 0xE0E7
7 'd 70 :RD = #1 16'b 1101101011011001; // -9511 0xDAD9
7 'd 71 :RD = #1 16'b 1101010011100010; // -11038 0xD4E2
7 'd 72 :RD = #1 16'b 1100111100000101; // -12539 0xCF05
7 'd 73 :RD = #1 16'b 1100100101000111; // -14009 0xC947
7 'd 74 :RD = #1 16'b 1100001110101010; // -15446 0xC3AA
7 'd 75 :RD = #1 16'b 1011111000110011; // -16845 0xBE33
7 'd 76 :RD = #1 16'b 1011100011100100; // -18204 0xB8E4
7 'd 77 :RD = #1 16'b 1011001111000001; // -19519 0xB3C1
7 'd 78 :RD = #1 16'b 1010111011001101; // -20787 0xAECD
7 'd 79 :RD = #1 16'b 1010101000001100; // -22004 0xAA0C
7 'd 80 :RD = #1 16'b 1010010101111111; // -23169 0xA57F
7 'd 81 :RD = #1 16'b 1010000100101010; // -24278 0xA12A
7 'd 82 :RD = #1 16'b 1001110100001111; // -25329 0x9D0F
7 'd 83 :RD = #1 16'b 1001100100110010; // -26318 0x9932
7 'd 84 :RD = #1 16'b 1001010110010100; // -27244 0x9594
7 'd 85 :RD = #1 16'b 1001001000110111; // -28105 0x9237
7 'd 86 :RD = #1 16'b 1000111100011111; // -28897 0x8F1F
7 'd 87 :RD = #1 16'b 1000110001001011; // -29621 0x8C4B
7 'd 88 :RD = #1 16'b 1000100111000000; // -30272 0x89C0
7 'd 89 :RD = #1 16'b 1000011101111101; // -30851 0x877D
7 'd 90 :RD = #1 16'b 1000010110000100; // -31356 0x8584
7 'd 91 :RD = #1 16'b 1000001111010111; // -31785 0x83D7
7 'd 92 :RD = #1 16'b 1000001001110111; // -32137 0x8277
7 'd 93 :RD = #1 16'b 1000000101100100; // -32412 0x8164
7 'd 94 :RD = #1 16'b 1000000010011111; // -32609 0x809F
7 'd 95 :RD = #1 16'b 1000000000101001; // -32727 0x8029
7 'd 96 :RD = #1 16'b 1000000000000001; // -32767 0x8001
7 'd 97 :RD = #1 16'b 1000000000101001; // -32727 0x8029
7 'd 98 :RD = #1 16'b 1000000010011111; // -32609 0x809F
7 'd 99 :RD = #1 16'b 1000000101100100; // -32412 0x8164
7 'd 100 :RD = #1 16'b 1000001001110111; // -32137 0x8277
7 'd 101 :RD = #1 16'b 1000001111010111; // -31785 0x83D7
7 'd 102 :RD = #1 16'b 1000010110000100; // -31356 0x8584
7 'd 103 :RD = #1 16'b 1000011101111101; // -30851 0x877D
7 'd 104 :RD = #1 16'b 1000100111000000; // -30272 0x89C0
7 'd 105 :RD = #1 16'b 1000110001001011; // -29621 0x8C4B
7 'd 106 :RD = #1 16'b 1000111100011111; // -28897 0x8F1F
7 'd 107 :RD = #1 16'b 1001001000110111; // -28105 0x9237
7 'd 108 :RD = #1 16'b 1001010110010100; // -27244 0x9594
7 'd 109 :RD = #1 16'b 1001100100110010; // -26318 0x9932
7 'd 110 :RD = #1 16'b 1001110100001111; // -25329 0x9D0F
7 'd 111 :RD = #1 16'b 1010000100101010; // -24278 0xA12A
7 'd 112 :RD = #1 16'b 1010010101111111; // -23169 0xA57F
7 'd 113 :RD = #1 16'b 1010101000001100; // -22004 0xAA0C
7 'd 114 :RD = #1 16'b 1010111011001101; // -20787 0xAECD
7 'd 115 :RD = #1 16'b 1011001111000001; // -19519 0xB3C1
7 'd 116 :RD = #1 16'b 1011100011100100; // -18204 0xB8E4
7 'd 117 :RD = #1 16'b 1011111000110011; // -16845 0xBE33
7 'd 118 :RD = #1 16'b 1100001110101010; // -15446 0xC3AA
7 'd 119 :RD = #1 16'b 1100100101000111; // -14009 0xC947
7 'd 120 :RD = #1 16'b 1100111100000101; // -12539 0xCF05
7 'd 121 :RD = #1 16'b 1101010011100010; // -11038 0xD4E2
7 'd 122 :RD = #1 16'b 1101101011011001; // -9511 0xDAD9
7 'd 123 :RD = #1 16'b 1110000011100111; // -7961 0xE0E7
7 'd 124 :RD = #1 16'b 1110011100001000; // -6392 0xE708
7 'd 125 :RD = #1 16'b 1110110100111001; // -4807 0xED39
7 'd 126 :RD = #1 16'b 1111001101110101; // -3211 0xF375
7 'd 127 :RD = #1 16'b 1111100110111001; // -1607 0xF9B9
default : RD = #1 0;
endcase
endmodule
由公式计算出频率增量CNT,取整,转化为二进制,然后修改原代码中频率计算的部分,由输入为频率字赋对应的值。
// mc_dds
// multi-cycle dds module
module mc_dds(
CLK , // clock posedge
RST , // reset posedge
FREQIN, // input frequency word
FREQEN, // frequency word input enable
DDSEN , // multi-cycle dds work enable
OUTVD , // dds output valid
DDSOUT); // dds output wave
input CLK ;
input RST ;
input [32-1:0] FREQIN;
input FREQEN;
input DDSEN ;
output OUTVD ;
output [11 :0] DDSOUT;
// work enable delay line
reg ddsen_R1, ddsen_R2, ddsen_R3;
reg [32-1:0] freqin_R ;
reg [32-1:0] phase_acc_R ;
reg [16-1:0] DDSOUT ;
wire [16-1:0] rom_rdW ; // rom read data
wire [7-1:0] rom_raW ; // rom read address
// code template
// always @ (posedge CLK or posedge RST) begin
// if(RST) begin
// end
// else begin
// end
// end
always @ (posedge CLK or posedge RST) begin
if(RST) begin
ddsen_R1 <= 1'b0;
ddsen_R2 <= 1'b0;
ddsen_R3 <= 1'b0;
end
else begin
ddsen_R1 <= DDSEN ;
ddsen_R2 <= ddsen_R1;
ddsen_R3 <= ddsen_R2;
end
end
assign OUTVD = ddsen_R3;
// dds working pipeline
// OUTVD
// DDSEN | ddsen_R1 |ddsen_R2 |ddsen_R3
// | phase_acc_R |
// | rom_raW | rom_rdW | DDSOUT
always @ (posedge CLK or posedge RST) begin
if(RST) begin
phase_acc_R <= 0;
freqin_R <= 0;
end
else begin
if(FREQEN) begin
case(FREQIN[24:22])
3'b000:freqin_R <=32'b00000001100110011001100110011010;
3'b001:freqin_R <=32'b00000011001100110011001100110011;
3'b010:freqin_R <=32'b00000100110011001100110011001101;
3'b011:freqin_R <=32'b00000110011001100110011001100110;
3'b100:freqin_R <=32'b00001000000000000000000000000000;
3'b101:freqin_R <=32'b00001011001100110011001100110011;
3'b110:freqin_R <=32'b00001110011001100110011001100110;
3'b111:freqin_R <=32'b00010001100110011001100110011010;
endcase // if(FREQEN)
end // if(FREQEN)
if(DDSEN) begin
phase_acc_R <= phase_acc_R + freqin_R;
end // if(DDSEN)
if(ddsen_R2) begin
DDSOUT <= rom_rdW;
end
end
end
assign rom_raW = phase_acc_R[32-1: 32-1-7+1];
sine_rom U_sine_rom(
.CLK (CLK ), // clock
.RA (rom_raW ), // read address
.RD (rom_rdW )); // read data
endmodule // mc_dds();
DAC 接口模块 转换2补码为无符号数 /
// input is signed , output is unsigned
module DAC_interface(
CLKIN , // input clk
DATIN , // input data
SCALE , // scale factor, right shift the data
DAT2DAC ); // data to dac
input CLKIN;
input [12-1:0] DATIN ;
input [2-1:0] SCALE ;
output [12-1:0] DAT2DAC ;
reg [12-1:0] DAT2DAC ;
reg [12-1:0] datin_R1;
always @ (posedge CLKIN) begin
datin_R1 [11] <= ~ DATIN[11]; // inverse the msb to unsigned
datin_R1 [10: 0] <= DATIN[10:0];
DAT2DAC <= (datin_R1 >> SCALE);
end
endmodule
位宽扩展填充模块 /
module bus_LSB_staff_zero(
IN ,
OUT );
parameter INWL = 8;
parameter OUTWL = 16;
input [INWL -1:0] IN ;
output [OUTWL-1:0] OUT;
assign OUT[OUTWL-1:OUTWL - INWL ] = IN;
assign OUT[INWL-1:0] = 0;
endmodule
顶层结构:
RTL:
0.5MHz:000
1MHz:001
1.5MHz:010
思考题
• 在本例中,ADC的20MHz时钟和DAC的 80MHz时钟,是从一个晶振用PLL分出来的。
• 这种时钟叫做同步时钟
• 现在的问题是,出于提高计算密度和需求, 我们想用那个80MHz的时钟处理ADC送入 的20MHz的数据流。
• 应该怎么做?
用时间基准电路从80M 的时钟里产生20M的时钟用于产生ADC的时钟,使整个电路都使用同一个时钟,保证电路的准确。