目录
5、把数据导入到Matlab(具体操作见上次的文章验证方法)
一、实验目标
- 掌握并行DAC、ADC的接口时序
- 用DDS合成信号,经过DAC输出
- 用ADC采集信号
- 使用SignalTAP在时域观察信号
- 使用Matlab在频域观察信号
二、时钟方案
- 使用FPGA片内的锁相环 – 由50MHz晶振的时钟信号 – 倍频得到80MHz的DAC时钟 – 分频得到20MHz的ADC时钟
- 用FPGA的逻辑对内部的80MHz时钟反相作为DAC 芯片时钟,用于满足DAC数据的建立-保持时序
- ADC芯片在时钟的上跳锁存数据
- ADC时钟同样进行反相,作为ADC接口逻辑的驱动时钟
- 注意:这种用FPGA的内部逻辑电路进行ADC、 DAC的时钟相位调整的方法,其时钟的抖动和相位 噪声不好。高质量设计中,通常用专门的时钟管理 芯片完成时钟的相位调节
三、总模块
- 该设计由一个PLL从50MHz晶振生成80MHz的DAC时钟、 20MHzADC时钟
- 一个单周期的DDS生成补码正弦波,转成无符号后送至DAC
- 拨码开关SW9 SW8 用于控制DAC的输出幅度 – 其余的SW开关用于控制DDS频率字
四、各个模块分析
1、pll_DAC_ADC【用于产生dac与adc的clk,与原信号相比,c0为原本的8/5,c1比率为2/5,直流占比均为50%】
2、DAC发生器(数模转换)<1>mc_dds
(1)输入控制
频率输入(FREQIN)低8位由0-7位拨码开关控制,其他32-8=24位接地为0。
使能接高电压一直导通。复位信号接地始终为0,不复位。
(2)输出信号
OUTTV用于检测该模块是否正常运行。
DDSOUT是用于产生输出波形。
<2>DAC_interface
DAC 接口模块 转换2补码为无符号数 。输入信号为有符号数,输出符号为无符号数。
(1)输入信号
SCALE比例因子,右移数据
(2)输出信号
数模转换的结果,配置的管脚与仪器运转有关,不更改就好。
注:为取反信号。
3、ADC发生器(模数转换)
信号:
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 (ADC待机,设置0使ADC运行)
DOUT // data out
五、学生实验
修改代码完成以下任务
1、把DDS的rom改成16比特,高12位送给DAC
ROM波形数据更改:方法
dds模块:更改输出数据bit数
dac模块输入更改数据bit数:
rom高12位送给DAC
2、不要忘记缩放DAC的输出缩放
3、生成不同频率的正弦波
– 仍然保持80MHz的DAC时钟速率 50MHz*8/5=80MHz
– 用3比特的拨码开关设定输出正弦波频率 (类似3-8译码器)
– 000~ 0.5MHz、001 ~ 1MHz 、010 ~ 1.5MHz、011 ~ 2MHz – 100~ 2.5MHz、101 ~ 3.5MHz、110 ~ 4.5MHz、111 ~ 5.5MHz
4、使用20MHz的ADC采样频率(保持不变)
5、把数据导入到Matlab(具体操作见上次的文章验证方法)
– 观察频谱纯度
– 确认你生成的信号频率正确
000~ 0.5MHz
001 ~ 1MHz
010 ~ 1.5MHz
011 ~ 2MHz
100~ 2.5MHz
101 ~ 3.5MHz
110 ~ 4.5MHz
111 ~ 5.5MHz
模块代码
// 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 [16-1 :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
freqin_R[31:30] <= FREQIN[31:30];
freqin_R[29:28] <= 0;
case(FREQIN[24:22])
3'b 000:freqin_R[27:0] <= 32'b0000_0001_1001_1001_1001_1001_1001_1010;
3'b 001:freqin_R[27:0] <= 32'b0000_0011_0011_0011_0011_0011_0011_0011;
3'b 010:freqin_R[27:0] <= 32'b0000_0100_1100_1100_1100_1100_1100_1101;
3'b 011:freqin_R[27:0] <= 32'b0000_0110_0110_0110_0110_0110_0110_0110;
3'b 100:freqin_R[27:0] <= 32'b0000_1000_0000_0000_0000_0000_0000_0000;
3'b 101:freqin_R[27:0] <= 32'b0000_1011_0011_0011_0011_0011_0011_0011;
3'b 110:freqin_R[27:0] <= 32'b0000_1110_0110_0110_0110_0110_0110_0110;
3'b 111:freqin_R[28:0] <= 32'b0001_0001_1001_1001_1001_1001_1001_1010;
endcase // 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();
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 接口模块 转换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
六、思考题
- 在本例中,ADC的20MHz时钟和DAC的 80MHz时钟,是从一个晶振用PLL分出来的。 这种时钟叫做同步时钟
- 现在的问题是,出于提高计算密度和需求, 我们想用那个80MHz的时钟处理ADC送入的20MHz的数据流。 应该怎么做?
用时间基准电路从80M 的时钟里产生20M的时钟用于产生ADC的时钟,使整个电路都使用同一个时钟,保证电路的准确。